Quick Start Guide¶
This guide will walk you through building your first optimization model with LumiX.
A Simple Production Planning Problem¶
Let’s solve a classic production planning problem: maximizing profit while respecting resource constraints.
Problem Description¶
We manufacture two products, each requiring different amounts of two resources:
Product A: Profit = $30/unit, requires 2 hours labor and 1 kg material
Product B: Profit = $40/unit, requires 1 hour labor and 2 kg material
Available resources:
Labor: 100 hours
Material: 80 kg
Goal: Maximize total profit.
Step 1: Define Your Data¶
First, let’s define our data using simple Python classes:
from dataclasses import dataclass
@dataclass
class Product:
id: str
name: str
profit: float
labor_required: float
material_required: float
# Define our products
products = [
Product("A", "Product A", profit=30, labor_required=2, material_required=1),
Product("B", "Product B", profit=40, labor_required=1, material_required=2),
]
# Define resource capacities
labor_capacity = 100
material_capacity = 80
Step 2: Build the Model¶
Now, let’s build the optimization model using LumiX:
from lumix import (
LXModel,
LXVariable,
LXConstraint,
LXLinearExpression,
)
# Decision variable: how much to produce of each product
production = (
LXVariable[Product, float]("production")
.continuous()
.bounds(lower=0) # Can't produce negative amounts
.indexed_by(lambda p: p.id) # Index by product ID
.from_data(products) # Use our product data
)
# Create the model
model = LXModel("production_plan").add_variable(production)
# Objective: Maximize total profit
model.maximize(
LXLinearExpression()
.add_term(production, lambda p: p.profit)
)
# Constraint: Labor capacity
model.add_constraint(
LXConstraint("labor_capacity")
.expression(
LXLinearExpression()
.add_term(production, lambda p: p.labor_required)
)
.le()
.rhs(labor_capacity)
)
# Constraint: Material capacity
model.add_constraint(
LXConstraint("material_capacity")
.expression(
LXLinearExpression()
.add_term(production, lambda p: p.material_required)
)
.le()
.rhs(material_capacity)
)
Step 3: Solve the Model¶
Use the optimizer to solve the model:
from lumix import LXOptimizer
# Create optimizer and select solver
optimizer = LXOptimizer().use_solver("ortools")
# Solve the model
solution = optimizer.solve(model)
# Check if we found an optimal solution
if solution.is_optimal():
print(f"Optimal profit: ${solution.objective_value:.2f}")
# Get the production quantities
for product in products:
quantity = solution.variables["production"][product.id]
print(f"Produce {quantity:.2f} units of {product.name}")
else:
print(f"No optimal solution found. Status: {solution.status}")
Complete Example¶
Here’s the complete code in one place:
from dataclasses import dataclass
from lumix import (
LXModel,
LXVariable,
LXConstraint,
LXLinearExpression,
LXOptimizer,
)
# Step 1: Define data
@dataclass
class Product:
id: str
name: str
profit: float
labor_required: float
material_required: float
products = [
Product("A", "Product A", profit=30, labor_required=2, material_required=1),
Product("B", "Product B", profit=40, labor_required=1, material_required=2),
]
labor_capacity = 100
material_capacity = 80
# Step 2: Build model
production = (
LXVariable[Product, float]("production")
.continuous()
.bounds(lower=0)
.indexed_by(lambda p: p.id)
.from_data(products)
)
model = (
LXModel("production_plan")
.add_variable(production)
.maximize(
LXLinearExpression()
.add_term(production, lambda p: p.profit)
)
)
model.add_constraint(
LXConstraint("labor_capacity")
.expression(
LXLinearExpression()
.add_term(production, lambda p: p.labor_required)
)
.le()
.rhs(labor_capacity)
)
model.add_constraint(
LXConstraint("material_capacity")
.expression(
LXLinearExpression()
.add_term(production, lambda p: p.material_required)
)
.le()
.rhs(material_capacity)
)
# Step 3: Solve
optimizer = LXOptimizer().use_solver("ortools")
solution = optimizer.solve(model)
if solution.is_optimal():
print(f"Optimal profit: ${solution.objective_value:.2f}")
for product in products:
quantity = solution.variables["production"][product.id]
print(f"Produce {quantity:.2f} units of {product.name}")
Expected Output¶
Optimal profit: $2000.00
Produce 20.00 units of Product A
Produce 30.00 units of Product B
Understanding the Code¶
Key Concepts¶
Variables with Indexing
LXVariable[Product, float]("production")
.indexed_by(lambda p: p.id)
.from_data(products)
This creates one decision variable for each product, automatically indexed by product ID.
The type annotation [Product, float] provides IDE autocomplete and type checking.
Data-Driven Coefficients
LXLinearExpression()
.add_term(production, lambda p: p.profit)
Coefficients are computed from your data using lambda functions. This is type-safe and eliminates manual loops and error-prone indexing.
Fluent API
All LumiX objects support method chaining for readable, declarative code:
model = (
LXModel("name")
.add_variable(var1)
.add_variable(var2)
.add_constraint(constraint1)
.maximize(objective)
)
Switching Solvers¶
LumiX makes it easy to switch between solvers. Just change the solver name:
# Use OR-Tools (free)
optimizer = LXOptimizer().use_solver("ortools")
# Use Gurobi (requires license)
optimizer = LXOptimizer().use_solver("gurobi")
# Use CPLEX (requires license)
optimizer = LXOptimizer().use_solver("cplex")
# Use GLPK (free)
optimizer = LXOptimizer().use_solver("glpk")
The rest of your code stays exactly the same!
Model Summary¶
View a summary of your model:
print(model.summary())
Output:
Model: production_plan
Variables: 1 family (2 instances)
Constraints: 2
Objective: MAXIMIZE
Next Steps¶
Now that you’ve built your first model, explore:
Examples: See the
examples/directory for more complex scenariosUser Guide: Learn about advanced features (coming soon)
API Reference: Detailed API documentation (coming soon)
Available Solvers: Learn about different solver capabilities
Common Patterns¶
Integer Variables¶
For integer decisions (e.g., number of trucks):
trucks = (
LXVariable[Route, int]("trucks")
.integer()
.bounds(lower=0, upper=10)
.indexed_by(lambda r: r.id)
.from_data(routes)
)
Binary Variables¶
For yes/no decisions (e.g., facility open/closed):
is_open = (
LXVariable[Facility, int]("is_open")
.binary()
.indexed_by(lambda f: f.id)
.from_data(facilities)
)
Multi-Dimensional Indexing¶
For decisions indexed by multiple dimensions:
from lumix import LXCartesianProduct
# Assignment of drivers to shifts on dates
assignment = (
LXVariable[tuple[Driver, Date, Shift], int]("assignment")
.binary()
.indexed_by(lambda item: (item[0].id, item[1].id, item[2].id))
.from_data(LXCartesianProduct(drivers, dates, shifts))
)
Getting Help¶
Check the examples in the repository
Read the API documentation
Open an issue on GitHub: https://github.com/tdelphi1981/LumiX/issues