Production Planning Example¶
Overview¶
This example demonstrates LumiX’s single-model indexing feature, which allows variables and constraints to be indexed directly by data model instances rather than manual integer indices.
The production planning problem is a fundamental optimization problem in operations research, making it an ideal introduction to data-driven modeling with LumiX.
Problem Description¶
A manufacturing company produces multiple products, each requiring different amounts of limited resources (labor hours, machine hours, raw materials).
Objective: Maximize total profit from production.
Constraints:
Resource capacity limits (can’t exceed available labor, machine time, materials)
Minimum production requirements (must meet customer orders)
Non-negativity (can’t produce negative quantities)
Mathematical Formulation¶
Decision Variables:
where \(x_p\) represents the production quantity for product \(p\).
Objective Function:
where \(\text{profit}_p = \text{selling\_price}_p - \text{unit\_cost}_p\).
Constraints:
Resource Capacity:
\[\sum_{p \in \text{Products}} \text{usage}_{p,r} \cdot x_p \leq \text{capacity}_r, \quad \forall r \in \text{Resources}\]Minimum Production:
\[x_p \geq \text{min\_production}_p, \quad \forall p \in \text{Products}\]
Key Features¶
Single-Model Indexing¶
Variables are indexed directly by Product instances:
- Production planning and scheduling
- Resource allocation problems
- Portfolio optimization
- Supply chain optimization
- Any problem with homogeneous decision variables indexed by entities
Learning Objectives:
Key Points:
LXVariable[Product, float]creates a variable family.indexed_by(lambda p: p.id)specifies the index key.from_data(PRODUCTS)auto-creates one variable per productNo manual loops or index management needed
Data-Driven Coefficients¶
Coefficients are extracted from data using lambda functions:
See Also:
- Example 02 (driver_scheduling): Multi-model indexing with cartesian products
- Example 04 (basic_lp): Simpler introduction to LumiX basics
- User Guide: Single-Model Indexing section
"""
The lambda p: p.selling_price - p.unit_cost extracts profit per unit directly
from each Product instance.
Automatic Expression Expansion¶
Expressions automatically sum over all indexed data:
def build_production_model() -> LXModel:
"""Build the production planning optimization model.
This function constructs a linear programming model to maximize profit
from production while respecting resource capacity constraints and minimum
The expression sums resource usage across all products automatically.
Constraint Families¶
Similar constraints can be created as families indexed by data:
Example:
>>> model = build_production_model()
>>> print(model.summary())
>>> optimizer = LXOptimizer().use_solver("ortools")
>>> solution = optimizer.solve(model)
Notes:
The data-driven approach means all coefficients are extracted from
the PRODUCTS and RESOURCES data using lambda functions, making the
This creates one minimum production constraint per product.
Type-Safe Solution Access¶
Solutions are accessed using the same indices as the original data:
for product in PRODUCTS:
qty = solution.variables["production"][product.id]
profit = (product.selling_price - product.unit_cost) * qty
Running the Example¶
Prerequisites:
pip install lumix[cplex] # or ortools, gurobi, glpk
Run:
cd examples/01_production_planning
python production_planning.py
Expected Output:
============================================================
OptiXNG Example: Production Planning
============================================================
Status: optimal
Optimal Profit: $3,465.00
Production Plan:
------------------------------------------------------------
Widget A : 10.0 units (profit: $500.00)
Widget B : 8.6 units (profit: $600.28)
Gadget X : 8.0 units (profit: $520.00)
Gadget Y : 23.7 units (profit: $1,186.50)
Premium Z : 3.0 units (profit: $300.00)
Complete Code Walkthrough¶
Step 1: Define Data Models¶
@dataclass
class Product:
"""Represents a product that can be manufactured.
This class models a product with its economic and resource consumption
characteristics. Each product has a profit margin (selling_price - unit_cost)
and requires various resources for production.
Attributes:
id: Unique identifier for the product.
name: Human-readable product name.
selling_price: Revenue per unit sold, in dollars.
unit_cost: Total production cost per unit (materials + labor), in dollars.
labor_hours: Labor time required per unit, in hours.
machine_hours: Machine time required per unit, in hours.
material_units: Raw material quantity required per unit.
min_production: Minimum production quantity to meet customer orders.
Example:
>>> widget = Product(
... id=1, name="Widget A", selling_price=100.0,
... unit_cost=50.0, labor_hours=5.0, machine_hours=3.0,
... material_units=2.0, min_production=10
... )
>>> profit_margin = widget.selling_price - widget.unit_cost
>>> print(f"Profit: ${profit_margin}")
Profit: $50.0
"""
id: int
name: str
selling_price: float # $ per unit
unit_cost: float # $ per unit (materials + labor)
labor_hours: float # hours per unit
machine_hours: float # hours per unit
material_units: float # units of raw material per product unit
min_production: int # minimum units to produce (customer orders)
Step 2: Create Variables¶
- Production planning and scheduling
- Resource allocation problems
- Portfolio optimization
- Supply chain optimization
- Any problem with homogeneous decision variables indexed by entities
Learning Objectives:
Step 3: Set Objective¶
See Also:
- Example 02 (driver_scheduling): Multi-model indexing with cartesian products
- Example 04 (basic_lp): Simpler introduction to LumiX basics
- User Guide: Single-Model Indexing section
"""
from lumix import LXConstraint, LXLinearExpression, LXModel, LXOptimizer, LXSolution, LXVariable
Step 4: Add Constraints¶
Resource Capacity:
solver_to_use = "ortools"
# ==================== MODEL BUILDING ====================
def build_production_model() -> LXModel:
"""Build the production planning optimization model.
This function constructs a linear programming model to maximize profit
from production while respecting resource capacity constraints and minimum
production requirements.
The model uses single-model indexing where variables are indexed directly
by Product instances, eliminating the need for manual index management.
Returns:
An LXModel instance containing:
Minimum Production:
Example:
>>> model = build_production_model()
>>> print(model.summary())
>>> optimizer = LXOptimizer().use_solver("ortools")
>>> solution = optimizer.solve(model)
Notes:
The data-driven approach means all coefficients are extracted from
the PRODUCTS and RESOURCES data using lambda functions, making the
model automatically adapt to changes in the data.
Step 5: Solve and Access Solution¶
optimizer = LXOptimizer().use_solver("cplex")
solution = optimizer.solve(model)
if solution.is_optimal():
for product in PRODUCTS:
qty = solution.variables["production"][product.id]
Learning Objectives¶
After completing this example, you should understand:
Variable Families: How one
LXVariableexpands to multiple solver variablesIndexing: How
.indexed_by()and.from_data()work togetherLambda Coefficients: How to extract coefficients from data instances
Automatic Summation: How expressions sum over all indexed instances
Constraint Families: How to create multiple similar constraints
Solution Mapping: How to access results using original indices
Common Patterns¶
Pattern 1: Variable Family from Data¶
variable = (
LXVariable[DataModel, float]("var_name")
.continuous() # or .integer() or .binary()
.bounds(lower=0, upper=100)
.indexed_by(lambda instance: instance.id)
.from_data(data_list)
)
Pattern 2: Expression with Lambda Coefficients¶
expr = (
LXLinearExpression()
.add_term(variable, coeff=lambda instance: instance.attribute)
)
Pattern 3: Constraint Family¶
model.add_constraint(
LXConstraint[DataModel]("constraint_name")
.expression(expr)
.ge() # or .le() or .eq()
.rhs(lambda instance: instance.threshold)
.from_data(data_list)
.indexed_by(lambda instance: instance.key)
)
Extending the Example¶
Try These Modifications¶
Add New Resource: Introduce a storage capacity constraint
Maximum Production: Add upper bounds on production quantities
Product Groups: Group products and add category-level constraints
Time Periods: Extend to multi-period production planning
Inventory: Add inventory variables and balance constraints
Next Steps¶
After mastering this example:
Example 02 (Driver Scheduling): Learn multi-model indexing with cartesian products
Example 03 (Facility Location): Binary variables and fixed costs
Example 04 (Basic LP): Even simpler introduction if needed
User Guide - Indexing: Deep dive into single and multi-model indexing
See Also¶
Related Examples:
Basic Linear Programming Example - Simpler introduction to LumiX basics
Driver Scheduling Example - Multi-model indexing
Facility Location Example - Binary variables and MIP
API Reference:
User Guide:
Single-Model Indexing - Single-model indexing
Variables Guide - Variable types and families
Constraints Guide - Constraint modeling
Files in This Example¶
production_planning.py- Main optimization model and solution displaysample_data.py- Data models (Product, Resource) and sample dataREADME.md- Detailed documentation and usage guide