Diet Optimization
Select foods to satisfy daily nutritional requirements at minimum cost.
Select foods to satisfy daily nutritional requirements at minimum cost.
Select foods to satisfy nutritional requirements at minimum cost.
What this template is for
Meal planning services, hospitals, and institutional food services need to design menus that meet nutritional requirements without overspending. This template models selecting from a small set of foods to satisfy daily bounds on calories, protein, fat, and sodium at minimum cost.
This is the classic “diet problem”, one of the earliest practical applications of linear programming originally formulated by economist George Stigler in 1945. This template uses RelationalAI’s prescriptive reasoning (optimization) capabilities to find the optimal food combination that meets all nutritional constraints at the lowest cost.
Prescriptive reasoning helps you:
- Reduce cost while still meeting nutrition guidelines.
- Guarantee compliance with min/max nutrient bounds.
- Scale decision-making from a handful of foods to large catalogs.
Who this is for
- You want a small, end-to-end example of prescriptive reasoning (optimization) with RelationalAI.
- You’re comfortable with basic Python and linear optimization concepts.
What you’ll build
- A semantic model of foods and nutrients using concepts and properties.
- A linear program that chooses non-negative servings per food.
- Nutrient bound constraints and a cost-minimization objective.
- A solver that uses the HiGHS backend to print a readable diet plan.
What’s included
- Model + solve script:
diet.py - Sample data:
data/foods.csv,data/nutrients.csv
Prerequisites
Access
- A Snowflake account that has the RAI Native App installed.
- A Snowflake user with permissions to access the RAI Native App.
Tools
- Python >= 3.10
Quickstart
Follow these steps to run the template with the included sample data.
-
Download the ZIP file for this template and extract it:
Terminal window curl -O https://private.relational.ai/templates/zips/v0.13/diet.zipunzip diet.zipcd diet -
Create and activate a virtual environment
Terminal window python -m venv .venvsource .venv/bin/activatepython -m pip install --upgrade pip -
Install dependencies
Terminal window python -m pip install . -
Configure Snowflake connection and RAI profile
Terminal window rai init -
Run the template
Terminal window python diet.py -
Expected output
Status: OPTIMALMinimum cost: $11.83Optimal diet:name amounthamburger 0.604514icecream 2.591319milk 6.970139
Template structure
.├─ README.md├─ pyproject.toml├─ diet.py # main runner / entrypoint└─ data/ # sample input data ├─ foods.csv └─ nutrients.csvStart here: diet.py
Sample data
Data files are in data/.
nutrients.csv
Defines nutrient bounds (min/max) for a single day.
| Column | Meaning |
|---|---|
name | Nutrient name (e.g., calories, protein) |
min | Minimum daily requirement |
max | Maximum daily allowance |
foods.csv
Lists foods, their cost, and nutrient quantities per serving.
Each nutrient in nutrients.csv.name is also a column in this file.
| Column | Meaning |
|---|---|
name | Food name |
cost | Cost per serving |
<nutrient columns> | Quantity per serving (e.g., calories, protein, fat, sodium) |
Model overview
The semantic model for this template is built around two concepts and one relationship.
Nutrient
A nutrient with minimum and maximum daily bounds.
| Property | Type | Identifying? | Notes |
|---|---|---|---|
name | string | Yes | Loaded as the key from data/nutrients.csv |
min | float | No | Minimum daily requirement |
max | float | No | Maximum daily allowance |
Food
A food item with a cost, plus a decision variable (amount) chosen by the solver.
| Property | Type | Identifying? | Notes |
|---|---|---|---|
name | string | Yes | Loaded from data/foods.csv.name and used for output labeling |
cost | float | No | Cost per serving |
amount | float | No | Continuous decision variable (servings, |
Relationships
This template uses a relationship (not just properties) to represent per-food nutrient quantities.
| Relationship | Schema (reading string fields) | Notes |
|---|---|---|
Food.nutrients | {Food} contains {qty:float} of {Nutrient} | Quantity per serving loaded from the nutrient columns in data/foods.csv |
How it works
This section walks through the highlights in diet.py.
Import libraries and configure inputs
This template uses Concept objects from relationalai.semantics to model foods and nutrients, and uses Solver and SolverModel from relationalai.semantics.reasoners.optimization to define and solve the linear program:
from pathlib import Path
import pandasfrom pandas import read_csv
from relationalai.semantics import Model, data, define, require, select, sumfrom relationalai.semantics.reasoners.optimization import Solver, SolverModel
# --------------------------------------------------# Configure inputs and create the model# --------------------------------------------------
DATA_DIR = Path(__file__).parent / "data"
# Disable pandas inference of string types. This ensures that string columns# in the CSVs are loaded as object dtype. This is only required when using# relationalai versions prior to v1.0.pandas.options.future.infer_string = False
# --------------------------------------------------# Define semantic model & load data# --------------------------------------------------
# Create a Semantics model container.model = Model("diet", config=globals().get("config", None), use_lqp=False)Define concepts and load CSV data
First, it declares Nutrient and Food concepts, loads nutrients.csv into Nutrient, and then uses define(...).where(...) to populate Food.nutrients from the nutrient columns in foods.csv:
# Nutrient concept: represents a nutrient with minimum and maximum daily requirements.Nutrient = model.Concept("Nutrient")Nutrient.name = model.Property("{Nutrient} is named {name:string}")Nutrient.min = model.Property("{Nutrient} has minimum daily requirement {min:float}")Nutrient.max = model.Property("{Nutrient} has maximum daily requirement {max:float}")
nutrient_csv = read_csv(DATA_DIR / "nutrients.csv")data(nutrient_csv).into(Nutrient, keys=["name"])
# Food concept: foods have a cost and contain nutrients in some quantity.Food = model.Concept("Food")Food.nutrients = model.Relationship("{Food} contains {qty:float} of {Nutrient}")Food.cost = model.Property("{Food} costs {cost:float}")
food_csv = read_csv(DATA_DIR / "foods.csv")food_data = data(food_csv)
# Create one Food entity per row in the food data and define its cost.food = Food.new(name=food_data.name)define(food, food.cost(food_data.cost))
# Define nutrient quantities for each food by iterating the nutrient columns.for nutrient_name in nutrient_csv.name: define(Food.nutrients(food, food_data[nutrient_name], Nutrient)).where( Nutrient.name == nutrient_name )Define decision variables, constraints, and objective
Next, it creates one continuous, non-negative decision variable per food (Food.x_amount), enforces nutrient bounds with require(...), and minimizes total cost:
# Create a continuous optimization model.s = SolverModel(model, "cont")
# Decision Variable: amount of each food (continuous, non-negative)Food.x_amount = model.Property("{Food} has {amount:float}")s.solve_for(Food.x_amount, name=Food.name, lower=0)
# Calculate total quantity of each nutrient across all foods: sum(qty * amount) per nutrient.nutrient_total = sum( Food.nutrients["qty"] * Food.x_amount).where( Food.nutrients == Nutrient).per(Nutrient)
# Constraint: nutrient totals must be within specified bounds.nutrient_bounds = require( nutrient_total >= Nutrient.min, nutrient_total <= Nutrient.max)s.satisfy(nutrient_bounds)
# Objective: minimize total costtotal_cost = sum(Food.cost * Food.x_amount)s.minimize(total_cost)Solve and print results
Finally, it solves with the HiGHS backend and prints only foods with a non-trivial amount (Food.x_amount > 0.001):
# Solve the model with a time limit of 60 seconds using the HiGHS solver.solver = Solver("highs")s.solve(solver, time_limit_sec=60)
print(f"Status: {s.termination_status}")print(f"Minimum cost: ${s.objective_value:.2f}")
# Select the foods with non-trivial amounts in the optimal solution.diet_plan = select(Food.name, Food.x_amount).where(Food.x_amount > 0.001).to_df()
print("\nOptimal diet:")print(diet_plan.to_string(index=False))Customize this template
Here are some ideas for how to customize and extend this template to fit your specific use case.
Use your own data
- Replace the CSVs in
data/with your own, keeping the same column names (or update the loading logic indiet.py). - Ensure
foods.csvincludes a column for every nutrient listed innutrients.csv.name.
Tune parameters
- Tighten or relax nutritional bounds by editing
data/nutrients.csv. - Add new nutrients by adding rows to
data/nutrients.csvand adding matching columns todata/foods.csv.
Extend the model
- Add constraints like maximum servings per food or food category requirements.
- Add an “integer servings” variant by making
Food.x_amountan integer variable (and adjusting the model type if needed).
Scale up and productionize
- Replace CSV ingestion with Snowflake sources.
- Write the resulting diet plan back to Snowflake after solving.
Troubleshooting
Why does authentication/configuration fail?
- Run
rai initto create/updateraiconfig.toml. - If you have multiple profiles, set
RAI_PROFILEor switch profiles in your config.
Why does the script fail to connect to the RAI Native App?
- Verify the Snowflake account/role/warehouse and
rai_app_nameare correct inraiconfig.toml. - Ensure the RAI Native App is installed and you have access.
Why do I get Status: INFEASIBLE?
- Check for impossible bounds (e.g.,
min > maxfor a nutrient). - Confirm that the foods collectively can meet each nutrient’s minimum without violating other maximums.
Why is the output diet empty?
- The script filters foods with
Food.x_amount > 0.001. If all values are tiny, inspect nutrient bounds and costs. - Confirm the CSVs were read correctly and contain rows.
What this template is for
Meal planning services, hospitals, and institutional food services need to design menus that meet nutritional requirements without overspending. This template models selecting from a small set of foods to satisfy daily bounds on calories, protein, fat, and sodium at minimum cost.
This is the classic “diet problem”, one of the earliest practical applications of linear programming originally formulated by economist George Stigler in 1945. This template uses RelationalAI’s Prescriptive reasoning capabilities to find the optimal food combination that meets all nutritional constraints at the lowest cost.
Prescriptive reasoning helps you:
- Reduce cost while still meeting nutrition guidelines.
- Guarantee compliance with min/max nutrient bounds.
- Scale decision-making from a handful of foods to large catalogs.
Who this is for
- You want a small, end-to-end example of prescriptive reasoning (optimization) with RelationalAI.
- You’re comfortable with basic Python and linear optimization concepts.
What you’ll build
- A semantic model of foods and nutrients using concepts and properties.
- A linear program that chooses non-negative servings per food.
- Nutrient bound constraints and a cost-minimization objective.
- A solver that uses the HiGHS backend to print a readable diet plan.
What’s included
- Model + solve script:
diet.py - Sample data:
data/foods.csv,data/nutrients.csv
Prerequisites
Access
- A Snowflake account that has the RAI Native App installed.
- A Snowflake user with permissions to access the RAI Native App.
Tools
- Python >= 3.10
Quickstart
Follow these steps to run the template with the included sample data.
-
Download the ZIP file for this template and extract it:
Terminal window curl -O https://private.relational.ai/templates/zips/v0.14/diet.zipunzip diet.zipcd diet -
Create and activate a virtual environment
Terminal window python -m venv .venvsource .venv/bin/activatepython -m pip install --upgrade pip -
Install dependencies
Terminal window python -m pip install . -
Configure Snowflake connection and RAI profile
Terminal window rai init -
Run the template
Terminal window python diet.py -
Expected output
Status: OPTIMALMinimum cost: $11.83Optimal diet:name amounthamburger 0.604514icecream 2.591319milk 6.970139
Template structure
.├─ README.md├─ pyproject.toml├─ diet.py # main runner / entrypoint└─ data/ # sample input data ├─ foods.csv └─ nutrients.csvStart here: diet.py
Sample data
Data files are in data/.
nutrients.csv
Defines nutrient bounds (min/max) for a single day.
| Column | Meaning |
|---|---|
name | Nutrient name (e.g., calories, protein) |
min | Minimum daily requirement |
max | Maximum daily allowance |
foods.csv
Lists foods, their cost, and nutrient quantities per serving.
Each nutrient in nutrients.csv.name is also a column in this file.
| Column | Meaning |
|---|---|
name | Food name |
cost | Cost per serving |
<nutrient columns> | Quantity per serving (e.g., calories, protein, fat, sodium) |
Model overview
The semantic model for this template is built around two concepts and one relationship.
Nutrient
A nutrient with minimum and maximum daily bounds.
| Property | Type | Identifying? | Notes |
|---|---|---|---|
name | string | Yes | Loaded as the key from data/nutrients.csv |
min | float | No | Minimum daily requirement |
max | float | No | Maximum daily allowance |
Food
A food item with a cost, plus a decision variable (amount) chosen by the solver.
| Property | Type | Identifying? | Notes |
|---|---|---|---|
name | string | Yes | Loaded from data/foods.csv.name and used for output labeling |
cost | float | No | Cost per serving |
amount | float | No | Continuous decision variable (servings, |
Relationships
This template uses a relationship (not just properties) to represent per-food nutrient quantities.
| Relationship | Schema (reading string fields) | Notes |
|---|---|---|
Food.nutrients | {Food} contains {qty:float} of {Nutrient} | Quantity per serving loaded from the nutrient columns in data/foods.csv |
How it works
This section walks through the highlights in diet.py.
Import libraries and configure inputs
This template uses Concept objects from relationalai.semantics to model foods and nutrients, and uses Solver and SolverModel from relationalai.semantics.reasoners.optimization to define and solve the linear program:
from pathlib import Path
import pandasfrom pandas import read_csv
from relationalai.semantics import Model, data, define, require, select, sumfrom relationalai.semantics.reasoners.optimization import Solver, SolverModel
# --------------------------------------------------# Configure inputs and create the model# --------------------------------------------------
DATA_DIR = Path(__file__).parent / "data"
# Disable pandas inference of string types. This ensures that string columns# in the CSVs are loaded as object dtype. This is only required when using# relationalai versions prior to v1.0.pandas.options.future.infer_string = False
# --------------------------------------------------# Define semantic model & load data# --------------------------------------------------
# Create a Semantics model container.model = Model("diet", config=globals().get("config", None))Define concepts and load CSV data
First, it declares Nutrient and Food concepts, loads nutrients.csv into Nutrient, and then uses define(...).where(...) to populate Food.nutrients from the nutrient columns in foods.csv:
# Nutrient concept: represents a nutrient with minimum and maximum daily requirements.Nutrient = model.Concept("Nutrient")Nutrient.name = model.Property("{Nutrient} is named {name:string}")Nutrient.min = model.Property("{Nutrient} has minimum daily requirement {min:float}")Nutrient.max = model.Property("{Nutrient} has maximum daily requirement {max:float}")
nutrient_csv = read_csv(DATA_DIR / "nutrients.csv")data(nutrient_csv).into(Nutrient, keys=["name"])
# Food concept: foods have a cost and contain nutrients in some quantity.Food = model.Concept("Food")Food.nutrients = model.Relationship("{Food} contains {qty:float} of {Nutrient}")Food.cost = model.Property("{Food} costs {cost:float}")
food_csv = read_csv(DATA_DIR / "foods.csv")food_data = data(food_csv)
# Create one Food entity per row in the food data and define its cost.food = Food.new(name=food_data.name)define(food, food.cost(food_data.cost))
# Define nutrient quantities for each food by iterating the nutrient columns.for nutrient_name in nutrient_csv.name: define(Food.nutrients(food, food_data[nutrient_name], Nutrient)).where( Nutrient.name == nutrient_name )Define decision variables, constraints, and objective
Next, it creates one continuous, non-negative decision variable per food (Food.x_amount), enforces nutrient bounds with require(...), and minimizes total cost:
# Create a continuous optimization model.s = SolverModel(model, "cont")
# Decision Variable: amount of each food (continuous, non-negative)Food.x_amount = model.Property("{Food} has {amount:float}")s.solve_for(Food.x_amount, name=Food.name, lower=0)
# Calculate total quantity of each nutrient across all foods: sum(qty * amount) per nutrient.nutrient_total = sum( Food.nutrients["qty"] * Food.x_amount).where( Food.nutrients == Nutrient).per(Nutrient)
# Constraint: nutrient totals must be within specified bounds.nutrient_bounds = require( nutrient_total >= Nutrient.min, nutrient_total <= Nutrient.max)s.satisfy(nutrient_bounds)
# Objective: minimize total costtotal_cost = sum(Food.cost * Food.x_amount)s.minimize(total_cost)Solve and print results
Finally, it solves with the HiGHS backend and prints only foods with a non-trivial amount (Food.x_amount > 0.001):
# Solve the model with a time limit of 60 seconds using the HiGHS solver.solver = Solver("highs")s.solve(solver, time_limit_sec=60)
print(f"Status: {s.termination_status}")print(f"Minimum cost: ${s.objective_value:.2f}")
# Select the foods with non-trivial amounts in the optimal solution.diet_plan = select(Food.name, Food.x_amount).where(Food.x_amount > 0.001).to_df()
print("\nOptimal diet:")print(diet_plan.to_string(index=False))Customize this template
Here are some ideas for how to customize and extend this template to fit your specific use case.
Use your own data
- Replace the CSVs in
data/with your own, keeping the same column names (or update the loading logic indiet.py). - Ensure
foods.csvincludes a column for every nutrient listed innutrients.csv.name.
Tune parameters
- Tighten or relax nutritional bounds by editing
data/nutrients.csv. - Add new nutrients by adding rows to
data/nutrients.csvand adding matching columns todata/foods.csv.
Extend the model
- Add constraints like maximum servings per food or food category requirements.
- Add an “integer servings” variant by making
Food.x_amountan integer variable (and adjusting the model type if needed).
Scale up and productionize
- Replace CSV ingestion with Snowflake sources.
- Write the resulting diet plan back to Snowflake after solving.
Troubleshooting
Why does authentication/configuration fail?
- Run
rai initto create/updateraiconfig.toml. - If you have multiple profiles, set
RAI_PROFILEor switch profiles in your config.
Why does the script fail to connect to the RAI Native App?
- Verify the Snowflake account/role/warehouse and
rai_app_nameare correct inraiconfig.toml. - Ensure the RAI Native App is installed and you have access.
Why do I get Status: INFEASIBLE?
- Check for impossible bounds (e.g.,
min > maxfor a nutrient). - Confirm that the foods collectively can meet each nutrient’s minimum without violating other maximums.
Why is the output diet empty?
- The script filters foods with
Food.x_amount > 0.001. If all values are tiny, inspect nutrient bounds and costs. - Confirm the CSVs were read correctly and contain rows.
What this template is for
Choosing a balanced diet that meets nutritional requirements while staying within a budget is a classic optimization problem. Given a set of foods with known costs and nutrient contents, and a set of nutrients with minimum and maximum daily intake bounds, the goal is to find the cheapest combination of foods that satisfies all nutritional constraints.
This template uses prescriptive reasoning to formulate and solve the diet problem as a linear program. Each food has a continuous decision variable representing how much of it to include. Constraints enforce that the total nutrient intake from the selected foods falls within the required bounds for every nutrient.
The template also demonstrates scenario analysis by scaling nutritional requirements up and down (0.8x, 1.0x, 1.2x) and solving independently for each scenario. This lets you compare how dietary cost changes as requirements become more or less restrictive.
Who this is for
- Data scientists and analysts learning prescriptive optimization with RelationalAI
- Operations researchers looking for a clean LP formulation example
- Anyone interested in nutritional planning or cost minimization problems
- Beginners who want to understand scenario analysis in optimization
What you’ll build
- A linear programming model that selects foods to minimize total cost
- Nutritional constraints ensuring minimum and maximum daily intake for calories, protein, fat, and sodium
- Scenario analysis that scales nutritional requirements and compares results across scenarios
What’s included
diet.py— Main script defining the optimization model, constraints, and scenario analysisdata/foods.csv— Food items with cost and nutrient content per servingdata/nutrients.csv— Nutrient names with minimum and maximum daily intake boundspyproject.toml— Python package configuration with dependencies
Prerequisites
Access
- A Snowflake account that has the RAI Native App installed.
- A Snowflake user with permissions to access the RAI Native App.
Tools
- Python >= 3.10
Quickstart
-
Download ZIP:
Terminal window curl -O https://docs.relational.ai/templates/zips/v1/diet.zipunzip diet.zipcd diet -
Create venv:
Terminal window python -m venv .venvsource .venv/bin/activatepython -m pip install --upgrade pip -
Install:
Terminal window python -m pip install . -
Configure:
Terminal window rai init -
Run:
Terminal window python diet.py -
Expected output:
Running scenario: nutrient_scaling = 0.8Status: OPTIMAL, Objective: $6.53Diet plan:name valuechicken 0.52milk 10.41icecream 1.37Running scenario: nutrient_scaling = 1.0Status: OPTIMAL, Objective: $8.20Diet plan:name valuechicken 0.65milk 13.01icecream 1.71Running scenario: nutrient_scaling = 1.2Status: OPTIMAL, Objective: $9.87Diet plan:name valuechicken 0.78milk 15.62icecream 2.06==================================================Scenario Analysis Summary==================================================scaling=0.8: OPTIMAL, cost=$6.53scaling=1.0: OPTIMAL, cost=$8.20scaling=1.2: OPTIMAL, cost=$9.87
Template structure
.├── README.md├── pyproject.toml├── diet.py└── data/ ├── foods.csv └── nutrients.csvHow it works
1. Define concepts and load data
The model defines two concepts: Nutrient (with min/max bounds) and Food (with cost and nutrient content). A ternary property links each food to its nutrient quantities:
Nutrient = model.Concept("Nutrient", identify_by={"name": String})Nutrient.min = model.Property(f"{Nutrient} has {Float:min}")Nutrient.max = model.Property(f"{Nutrient} has {Float:max}")
Food = model.Concept("Food", identify_by={"name": String})Food.cost = model.Property(f"{Food} has {Float:cost}")Food.contains = model.Property(f"{Food} contains {Nutrient} in {Float:qty}")2. Decision variables
Each food gets a continuous decision variable representing the amount to include in the diet:
Food.x_amount = model.Property(f"{Food} has {Float:amount}")problem.solve_for(Food.x_amount, name=Food.name, lower=0, populate=False)3. Constraints and objective
Nutritional constraints ensure total intake from all foods falls within bounds for each nutrient. The objective minimizes total food cost:
nutrient_qty = Float.ref()nutrient_total = sum(nutrient_qty * Food.x_amount).where(Food.contains(Nutrient, nutrient_qty)).per(Nutrient)problem.satisfy(model.require( nutrient_total >= Nutrient.min * scenario_value, nutrient_total <= Nutrient.max * scenario_value))problem.minimize(sum(Food.cost * Food.x_amount))4. Scenario analysis
The template solves three scenarios by scaling nutritional requirements to 80%, 100%, and 120% of their base values, demonstrating how tighter or looser requirements affect total cost.
Customize this template
- Add more foods or nutrients: Extend the CSV files with additional rows and columns. The model automatically picks up new data.
- Change scenario parameters: Modify
SCENARIO_VALUESto test different scaling factors or introduce entirely different scenario dimensions (e.g., budget caps). - Add dietary preferences: Introduce upper bounds on specific foods (e.g., limit red meat) or add binary variables to model food inclusion/exclusion.
- Weight objectives: Add a secondary objective term to penalize undesirable foods alongside cost minimization.
Troubleshooting
Problem is infeasible
The nutritional bounds may be too tight for the available foods. Check that at least one combination of foods can satisfy all min/max constraints simultaneously. Try relaxing the scaling factor to a lower value (e.g., 0.5).
rai init fails or connection errors
Ensure your Snowflake credentials are configured correctly and that the RAI Native App is installed on your account. Run rai init again and verify the connection settings.
ModuleNotFoundError for relationalai
Make sure you activated the virtual environment and ran python -m pip install . from the template directory. The pyproject.toml declares the required dependencies.
Unexpected zero values in solution
Foods with zero in the solution are not cost-effective given the constraints. This is expected behavior. If you want to force inclusion of specific foods, add a minimum bound on their decision variables.