Factory Production
Choose production quantities per machine-product pair to maximize profit while meeting minimum production requirements.
Choose production quantities per machine-product pair to maximize profit while meeting minimum production requirements.
Maximize profit from production with limited resource availability per factory.
Browse files
Browse files
Browse files
What this template is for
Manufacturing facilities must decide what to produce given limited resources. This template models a factory with multiple machines producing different products, where:
- Each machine has limited hours available and a machine-specific hourly operating cost.
- Each product has a selling price and a minimum production requirement.
- Different machines take different amounts of time to produce each product.
The goal is to find the optimal product mix—how much of each product to make on each machine—to maximize profit (revenue minus machine operating costs) while respecting machine capacity and meeting minimum production targets.
Who this is for
- You want a small, end-to-end example of prescriptive reasoning (optimization) using RelationalAI Semantics.
- You’re comfortable with basic Python and the idea of constraints + objectives.
What you’ll build
- A semantic model of machines, products, and machine-product production times.
- A linear program (LP) with one continuous decision variable per feasible machine-product pair.
- Capacity and minimum-production constraints.
- A profit-maximizing objective.
What’s included
factory_production.py— defines the semantic model, optimization problem, and prints a solutiondata/— sample CSV inputs (machines.csv,products.csv,production_times.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 using 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/factory_production.zipunzip factory_production.zipcd factory_production -
Create and activate a virtual environment:
Terminal window python -m venv .venvsource .venv/bin/activatepython -m pip install -U pip -
Install dependencies:
Terminal window python -m pip install . -
Initialize your RAI profile:
Terminal window rai init -
Run the template script:
Terminal window python factory_production.py -
Expected output:
Status: OPTIMAL Total profit: $20977.78
Production plan: machine product quantity Machine_A Widget 80.000000 Machine_B Device 38.888889 Machine_C Gadget 15.000000 Machine_C Widget 90.000000Template structure
.├─ README.md├─ pyproject.toml├─ factory_production.py # main runner / entrypoint└─ data/ # sample input data ├─ machines.csv ├─ products.csv └─ production_times.csvStart here: factory_production.py
Sample data
Data files are located in the data/ subdirectory.
machines.csv
| Column | Description |
|---|---|
id | Unique machine identifier |
name | Machine name |
hours_available | Hours available per period |
hourly_cost | Operating cost per hour ($) |
products.csv
| Column | Description |
|---|---|
id | Unique product identifier |
name | Product name |
price | Selling price per unit ($) |
min_production | Minimum units that must be produced |
production_times.csv
| Column | Description |
|---|---|
machine_id | Reference to machine |
product_id | Reference to product |
hours_per_unit | Hours required to produce one unit |
Model overview
The optimization model is built around four concepts.
Machine
| Property | Type | Identifying? | Notes |
|---|---|---|---|
id | int | Yes | Primary key loaded from machines.csv |
name | string | No | Used for output labels |
hours_available | float | No | Capacity for each machine |
hourly_cost | float | No | Cost term in the objective |
Product
| Property | Type | Identifying? | Notes |
|---|---|---|---|
id | int | Yes | Primary key loaded from products.csv |
name | string | No | Used for output labels |
price | float | No | Revenue per unit |
min_production | int | No | Minimum units required |
ProductionTime
| Property | Type | Identifying? | Notes |
|---|---|---|---|
machine | Machine | Part of compound key | Machine for the route |
product | Product | Part of compound key | Product for the route |
hours_per_unit | float | No | Time to make one unit |
Production (decision concept)
| Property | Type | Identifying? | Notes |
|---|---|---|---|
prod_time | ProductionTime | Yes | One variable per machine-product route |
quantity | float | No | Continuous decision variable, lower bounded by 0 |
How it works
This section walks through the highlights in factory_production.py.
Import libraries and configure inputs
This template uses Concept objects from relationalai.semantics to model machines, products, and production times, 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, sum, wherefrom relationalai.semantics.reasoners.optimization import Solver, SolverModel
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("factory", use_lqp=False)Define concepts and load CSV data
Next, it declares the Machine, Product, and ProductionTime concepts and loads the corresponding CSV tables. data(...).into(...) creates entities from machines.csv and products.csv, and where(...).define(...) joins production_times.csv onto those concepts:
# Machine concept: represents a production machine with available hours and hourly costMachine = model.Concept("Machine")Machine.id = model.Property("{Machine} has {id:int}")Machine.name = model.Property("{Machine} has {name:string}")Machine.hours_available = model.Property("{Machine} has {hours_available:float}")Machine.hourly_cost = model.Property("{Machine} has {hourly_cost:float}")
# Load machine data from CSV and create Machine entities.data(read_csv(DATA_DIR / "machines.csv")).into(Machine, keys=["id"])
# Product concept: represents a product with price and minimum production requirementsProduct = model.Concept("Product")Product.id = model.Property("{Product} has {id:int}")Product.name = model.Property("{Product} has {name:string}")Product.price = model.Property("{Product} has {price:float}")Product.min_production = model.Property("{Product} has {min_production:int}")
# Load product data from CSV and create Product entities.data(read_csv(DATA_DIR / "products.csv")).into(Product, keys=["id"])
# ProdTime concept: represents the time required to produce one unit of a product on a machineProdTime = model.Concept("ProductionTime")ProdTime.machine = model.Property("{ProductionTime} on {machine:Machine}")ProdTime.product = model.Property("{ProductionTime} of {product:Product}")ProdTime.hours_per_unit = model.Property("{ProductionTime} takes {hours_per_unit:float}")
# Load production time data from CSV.times_data = data(read_csv(DATA_DIR / "production_times.csv"))
# Define ProductionTime entities by joining machine/product IDs from the CSV with# the Machine and Product concepts.where( Machine.id == times_data.machine_id, Product.id == times_data.product_id,).define( ProdTime.new(machine=Machine, product=Product, hours_per_unit=times_data.hours_per_unit))Define decision variables, constraints, and objective
Then it creates one continuous, non-negative decision variable per machine–product route, adds machine-hour and minimum-production constraints with require(...), and maximizes profit:
# Decision concept: production quantities for each machine/productProduction = model.Concept("Production")Production.prod_time = model.Property("{Production} uses {prod_time:ProductionTime}")Production.x_quantity = model.Property("{Production} has {quantity:float}")
# Define one Production entity per machine-product ProductionTime record.define(Production.new(prod_time=ProdTime))
Prod = Production.ref()
s = SolverModel(model, "cont")
# Variable: production quantitys.solve_for( Production.x_quantity, name=["qty", Production.prod_time.machine.name, Production.prod_time.product.name], lower=0,)
# Constraint: total production hours per machine <= hours_availabletotal_hours = sum( Prod.quantity * Prod.prod_time.hours_per_unit).where( Prod.prod_time.machine == Machine).per(Machine)machine_limit = require(total_hours <= Machine.hours_available)s.satisfy(machine_limit)
# Constraint: total production per product >= min_productiontotal_produced = sum(Prod.quantity).where(Prod.prod_time.product == Product).per(Product)meet_minimum = require(total_produced >= Product.min_production)s.satisfy(meet_minimum)
# Objective: maximize profit (revenue - machine costs)revenue = sum(Production.x_quantity * Production.prod_time.product.price)machine_cost = sum( Production.x_quantity * Production.prod_time.hours_per_unit * Production.prod_time.machine.hourly_cost)profit = revenue - machine_costs.maximize(profit)Solve and print results
Finally, it solves using the HiGHS backend and prints only rows where Production.x_quantity > 0:
solver = Solver("highs")s.solve(solver, time_limit_sec=60)
print(f"Status: {s.termination_status}")print(f"Total profit: ${s.objective_value:.2f}")
plan = select( Production.prod_time.machine.name.alias("machine"), Production.prod_time.product.name.alias("product"), Production.x_quantity).where(Production.x_quantity > 0).to_df()
print("\nProduction plan:")print(plan.to_string(index=False))Customize this template
Use your own data
- Replace the CSV files under
data/. - Keep IDs consistent across files (
machine_id/product_idmust exist in the respective tables).
Extend the model
- Add maximum production limits, inventory constraints, or demand caps.
- Add setup/changeover costs and binary on/off decisions (MILP).
- Switch to integer quantities (or start from ../production_planning/README.md).
Troubleshooting
Connection/auth issues
- Re-run
rai initand confirm the selected profile is correct. - Ensure you have access to the RAI Native App in Snowflake.
ModuleNotFoundError when running the script
- Confirm your virtual environment is activated.
- Install the template dependencies from this folder:
python -m pip install .
CSV loading fails (missing file or column)
- Confirm the CSVs exist under
data/and the filenames match. - Ensure the headers match the expected schema:
machines.csv:id,name,hours_available,hourly_costproducts.csv:id,name,price,min_productionproduction_times.csv:machine_id,product_id,hours_per_unit
Status is INFEASIBLE
- Check that product minimums can be met with available machine hours.
- Ensure each required product has at least one route in
production_times.csv.
No rows printed in the production plan
- The output filters on
Production.x_quantity > 0. - If quantities are extremely small, print the full variable set or relax the filter.
Solver fails or returns an unexpected termination status
- Try re-running; transient connectivity issues can affect the solve step.
- If the solve is slow, reduce problem size (fewer machines/products/routes) or increase the time limit in
factory_production.py.
What this template is for
Manufacturing facilities must decide what to produce given limited resources. This template models a factory with multiple machines producing different products, where:
- Each machine has limited hours available and a machine-specific hourly operating cost.
- Each product has a selling price and a minimum production requirement.
- Different machines take different amounts of time to produce each product.
The goal is to find the optimal product mix—how much of each product to make on each machine—to maximize profit (revenue minus machine operating costs) while respecting machine capacity and meeting minimum production targets.
This template uses RelationalAI’s Prescriptive reasoning capabilities (optimization) to compute a profit-maximizing production plan that meets minimum requirements while respecting machine capacity.
Who this is for
- You want a small, end-to-end example of prescriptive reasoning (optimization) using RelationalAI Semantics.
- You’re comfortable with basic Python and the idea of constraints + objectives.
What you’ll build
- A semantic model of machines, products, and machine-product production times.
- A linear program (LP) with one continuous decision variable per feasible machine-product pair.
- Capacity and minimum-production constraints.
- A profit-maximizing objective.
What’s included
factory_production.py— defines the semantic model, optimization problem, and prints a solutiondata/— sample CSV inputs (machines.csv,products.csv,production_times.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 using 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/factory_production.zipunzip factory_production.zipcd factory_production -
Create and activate a virtual environment:
Terminal window python -m venv .venvsource .venv/bin/activatepython -m pip install -U pip -
Install dependencies:
Terminal window python -m pip install . -
Initialize your RAI profile:
Terminal window rai init -
Run the template script:
Terminal window python factory_production.py -
Expected output:
Status: OPTIMAL Total profit: $20977.78
Production plan: machine product quantity Machine_A Widget 80.000000 Machine_B Device 38.888889 Machine_C Gadget 15.000000 Machine_C Widget 90.000000Template structure
.├─ README.md├─ pyproject.toml├─ factory_production.py # main runner / entrypoint└─ data/ # sample input data ├─ machines.csv ├─ products.csv └─ production_times.csvStart here: factory_production.py
Sample data
Data files are located in the data/ subdirectory.
machines.csv
| Column | Description |
|---|---|
id | Unique machine identifier |
name | Machine name |
hours_available | Hours available per period |
hourly_cost | Operating cost per hour ($) |
products.csv
| Column | Description |
|---|---|
id | Unique product identifier |
name | Product name |
price | Selling price per unit ($) |
min_production | Minimum units that must be produced |
production_times.csv
| Column | Description |
|---|---|
machine_id | Reference to machine |
product_id | Reference to product |
hours_per_unit | Hours required to produce one unit |
Model overview
The optimization model is built around four concepts.
Machine
| Property | Type | Identifying? | Notes |
|---|---|---|---|
id | int | Yes | Primary key loaded from machines.csv |
name | string | No | Used for output labels |
hours_available | float | No | Capacity for each machine |
hourly_cost | float | No | Cost term in the objective |
Product
| Property | Type | Identifying? | Notes |
|---|---|---|---|
id | int | Yes | Primary key loaded from products.csv |
name | string | No | Used for output labels |
price | float | No | Revenue per unit |
min_production | int | No | Minimum units required |
ProductionTime
| Property | Type | Identifying? | Notes |
|---|---|---|---|
machine | Machine | Part of compound key | Machine for the route |
product | Product | Part of compound key | Product for the route |
hours_per_unit | float | No | Time to make one unit |
Production (decision concept)
| Property | Type | Identifying? | Notes |
|---|---|---|---|
prod_time | ProductionTime | Yes | One variable per machine-product route |
quantity | float | No | Continuous decision variable, lower bounded by 0 |
How it works
This section walks through the highlights in factory_production.py.
Import libraries and configure inputs
This template uses Concept objects from relationalai.semantics to model machines, products, and production times, 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, Relationship, data, define, require, select, sum, wherefrom relationalai.semantics.reasoners.optimization import Solver, SolverModel
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("factory")Define concepts and load CSV data
Next, it declares the Machine, Product, and ProductionTime concepts and loads the corresponding CSV tables. data(...).into(...) creates entities from machines.csv and products.csv, and where(...).define(...) joins production_times.csv onto those concepts:
# Machine concept: represents a production machine with available hours and hourly costMachine = model.Concept("Machine")Machine.id = model.Property("{Machine} has {id:int}")Machine.name = model.Property("{Machine} has {name:string}")Machine.hours_available = model.Property("{Machine} has {hours_available:float}")Machine.hourly_cost = model.Property("{Machine} has {hourly_cost:float}")
# Load machine data from CSV and create Machine entities.data(read_csv(DATA_DIR / "machines.csv")).into(Machine, keys=["id"])
# Product concept: represents a product with price and minimum production requirementsProduct = model.Concept("Product")Product.id = model.Property("{Product} has {id:int}")Product.name = model.Property("{Product} has {name:string}")Product.price = model.Property("{Product} has {price:float}")Product.min_production = model.Property("{Product} has {min_production:int}")
# Load product data from CSV and create Product entities.data(read_csv(DATA_DIR / "products.csv")).into(Product, keys=["id"])
# ProdTime concept: represents the time required to produce one unit of a product on a machineProdTime = model.Concept("ProductionTime")ProdTime.machine = model.Relationship("{ProductionTime} on {machine:Machine}")ProdTime.product = model.Relationship("{ProductionTime} of {product:Product}")ProdTime.hours_per_unit = model.Property("{ProductionTime} takes {hours_per_unit:float}")
# Load production time data from CSV.times_data = data(read_csv(DATA_DIR / "production_times.csv"))
# Define ProductionTime entities by joining machine/product IDs from the CSV with# the Machine and Product concepts.where( Machine.id == times_data.machine_id, Product.id == times_data.product_id,).define( ProdTime.new(machine=Machine, product=Product, hours_per_unit=times_data.hours_per_unit))Define decision variables, constraints, and objective
Then it creates one continuous, non-negative decision variable per machine–product route, adds machine-hour and minimum-production constraints with require(...), and maximizes profit:
# Decision concept: production quantities for each machine/productProduction = model.Concept("Production")Production.prod_time = model.Relationship("{Production} uses {prod_time:ProductionTime}")Production.x_quantity = model.Property("{Production} has {quantity:float}")
# Define one Production entity per machine-product ProductionTime record.define(Production.new(prod_time=ProdTime))
ProductionRef = Production.ref()
s = SolverModel(model, "cont")
# Variable: production quantitys.solve_for( Production.x_quantity, name=["qty", Production.prod_time.machine.name, Production.prod_time.product.name], lower=0,)
# Constraint: total production hours per machine <= hours_availabletotal_hours = sum( ProductionRef.x_quantity * ProductionRef.prod_time.hours_per_unit).where( ProductionRef.prod_time.machine == Machine).per(Machine)machine_limit = require(total_hours <= Machine.hours_available)s.satisfy(machine_limit)
# Constraint: total production per product >= min_productiontotal_produced = sum(ProductionRef.x_quantity).where(ProductionRef.prod_time.product == Product).per(Product)meet_minimum = require(total_produced >= Product.min_production)s.satisfy(meet_minimum)
# Objective: maximize profit (revenue - machine costs)revenue = sum(Production.x_quantity * Production.prod_time.product.price)machine_cost = sum( Production.x_quantity * Production.prod_time.hours_per_unit * Production.prod_time.machine.hourly_cost)profit = revenue - machine_costs.maximize(profit)Solve and print results
Finally, it solves using the HiGHS backend and prints only rows where Production.x_quantity > 0:
solver = Solver("highs")s.solve(solver, time_limit_sec=60)
print(f"Status: {s.termination_status}")print(f"Total profit: ${s.objective_value:.2f}")
plan = select( Production.prod_time.machine.name.alias("machine"), Production.prod_time.product.name.alias("product"), Production.x_quantity).where(Production.x_quantity > 0).to_df()
print("\nProduction plan:")print(plan.to_string(index=False))Customize this template
Use your own data
- Replace the CSV files under
data/. - Keep IDs consistent across files (
machine_id/product_idmust exist in the respective tables).
Extend the model
- Add maximum production limits, inventory constraints, or demand caps.
- Add setup/changeover costs and binary on/off decisions (MILP).
- Switch to integer quantities (or start from ../production_planning/README.md).
Troubleshooting
Connection/auth issues
- Re-run
rai initand confirm the selected profile is correct. - Ensure you have access to the RAI Native App in Snowflake.
ModuleNotFoundError when running the script
- Confirm your virtual environment is activated.
- Install the template dependencies from this folder:
python -m pip install .
CSV loading fails (missing file or column)
- Confirm the CSVs exist under
data/and the filenames match. - Ensure the headers match the expected schema:
machines.csv:id,name,hours_available,hourly_costproducts.csv:id,name,price,min_productionproduction_times.csv:machine_id,product_id,hours_per_unit
Status is INFEASIBLE
- Check that product minimums can be met with available machine hours.
- Ensure each required product has at least one route in
production_times.csv.
No rows printed in the production plan
- The output filters on
Production.x_quantity > 0. - If quantities are extremely small, print the full variable set or relax the filter.
Solver fails or returns an unexpected termination status
- Try re-running; transient connectivity issues can affect the solve step.
- If the solve is slow, reduce problem size (fewer machines/products/routes) or increase the time limit in
factory_production.py.
What this template is for
Manufacturing operations must decide how much of each product to produce at each factory to maximize profit, given limited resources and bounded demand. Each product has a production rate (units per hour of resource), a profit per unit, and a maximum demand. Each factory has a fixed number of available resource-hours.
This template formulates the problem as a linear program. Decision variables represent the quantity of each product to produce. Constraints ensure that total resource usage at each factory does not exceed availability and that production does not exceed demand. The objective maximizes total profit.
The template solves the problem independently per factory, demonstrating a scenario-based approach where each factory is treated as a separate optimization. This pattern is useful when factories operate autonomously or when you want to analyze each facility’s optimal production mix in isolation.
Who this is for
- Manufacturing planners optimizing production schedules
- Operations researchers learning resource-constrained profit maximization
- Data scientists exploring factory-level scenario analysis
- Beginners looking for a clean LP example with real-world context
What you’ll build
- A linear programming model that determines optimal production quantities per product
- Resource capacity constraints tied to factory availability
- Demand upper bounds on each product
- Per-factory scenario analysis with independent optimization
What’s included
factory_production.py— Main script defining the optimization model, constraints, and per-factory scenariosdata/factories.csv— Factory names and available resource-hoursdata/products.csv— Products with factory assignment, production rate, profit, and demand cappyproject.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
- RelationalAI Python SDK (
relationalai) >= 1.0.13
Quickstart
-
Download ZIP:
Terminal window curl -O https://docs.relational.ai/templates/zips/v1/factory_production.zipunzip factory_production.zipcd factory_production -
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 factory_production.py -
Expected output:
For factory: steel_factoryStatus: OPTIMAL, Profit: $192000.00Production plan:name valuebands 6000.0coils 1400.0For factory: amazing_breweryStatus: OPTIMAL, Profit: $6000.00Production plan:name valuestouts 1000.0ales 1000.0==================================================Factory Production Summary==================================================steel_factory: OPTIMAL, profit=$192000.00amazing_brewery: OPTIMAL, profit=$6000.00
Template structure
.├── README.md├── pyproject.toml├── factory_production.py└── data/ ├── factories.csv └── products.csvHow it works
1. Define concepts and load data
The model defines Factory (with available resource-hours) and Product (with factory assignment, production rate, profit, and demand). A relationship links products to their factory:
Factory = Concept("Factory", identify_by={"name": String})Factory.avail = Property(f"{Factory} has {Float:avail}")
Product = Concept("Product", identify_by={"name": String, "factory_name": String})Product.factory = Property(f"{Product} is produced by {Factory}")Product.rate = Property(f"{Product} has {Float:rate}")Product.profit = Property(f"{Product} has {Float:profit}")Product.demand = Property(f"{Product} has {Integer:demand}")2. Decision variables
Each product gets a continuous variable bounded between 0 and its demand cap:
problem.solve_for( Product.x_quantity, lower=0, upper=Product.demand, name=Product.name, where=[this_product], populate=False,)3. Constraints and objective
Resource usage at each factory must not exceed availability. The objective maximizes total profit:
profit = sum(Product.profit * Product.x_quantity).where(this_product)problem.maximize(profit)
problem.satisfy(model.require( sum(Product.x_quantity / Product.rate) <= Factory.avail).where(this_product, Factory.name(factory_name)))4. Per-factory scenario analysis
The template iterates over factories and solves an independent optimization for each, filtering products by their factory assignment. This allows comparison of optimal production plans across facilities.
Customize this template
- Add more factories and products: Extend the CSV files. The model automatically picks up new data and creates scenarios per factory.
- Shared resources across factories: Remove the per-factory loop and solve a single global problem with cross-factory resource constraints.
- Multi-period planning: Add a time dimension to model production across multiple periods with inventory carryover.
- Integer production: Change the variable type from continuous to integer if products must be produced in whole units.
Troubleshooting
Problem is infeasible
Check that factory availability is sufficient to produce at least some quantity of each product. If demand is high but resource-hours are too low, the bounded problem may still be feasible but produce small quantities.
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.
Products missing from solution
Products with zero quantity in the solution are not profitable enough to justify their resource usage. This is expected when resource availability is tight. Increase factory avail or reduce the production rate to see more products in the plan.