Modeling Basics
This guide walks you through creating your first optimization models with LunaModel.
Your First Model
Let's start with a simple example: selecting items with maximum value subject to a weight constraint (a knapsack problem).
Problem Description
You have 3 items with the following properties:
| Item | Value | Weight |
|---|---|---|
| A | 10 | 2 |
| B | 15 | 3 |
| C | 8 | 1 |
You can carry at most 4 units of weight. Which items should you select?
Define Variables
Create a binary variable for each item (1 = selected, 0 = not selected):
Add Constraints
Ensure total weight doesn't exceed capacity:
Set Objective
Maximize total value:
Complete Example
from luna_model import Model, Vtype, Sense
# Create model
model = Model()
# Variables
item_a = model.add_variable("item_a", vtype=Vtype.BINARY)
item_b = model.add_variable("item_b", vtype=Vtype.BINARY)
item_c = model.add_variable("item_c", vtype=Vtype.BINARY)
# Constraint
model.add_constraint(2*item_a + 3*item_b + 1*item_c <= 4)
# Objective
model.set_objective(10*item_a + 15*item_b + 8*item_c, sense=Sense.MAX)
print(model)
Common Modeling Patterns
Pattern 1 - Selection Problems
Choose k items from n options:
Pattern 2 - Assignment Problems
Assign n tasks to m workers:
n_tasks = 4
n_workers = 3
# assignment[i][j] = 1 if task i assigned to worker j
assignment = []
for i in range(n_tasks):
row = [model.add_variable(f"assign_{i}_{j}", vtype=Vtype.BINARY)
for j in range(n_workers)]
assignment.append(row)
# Each task assigned to exactly one worker
for i in range(n_tasks):
model.add_constraint(sum(assignment[i]) == 1)
# Worker capacity constraints (max 2 tasks per worker)
for j in range(n_workers):
model.add_constraint(sum(assignment[i][j] for i in range(n_tasks)) <= 2)
Pattern 3 - Ordering/Sequencing
Sequence items in order:
n = 4
# position[i][j] = 1 if item i is in position j
position = []
for i in range(n):
row = [model.add_variable(f"pos_{i}_{j}", vtype=Vtype.BINARY)
for j in range(n)]
position.append(row)
# Each item in exactly one position
for i in range(n):
model.add_constraint(sum(position[i]) == 1)
# Each position has exactly one item
for j in range(n):
model.add_constraint(sum(position[i][j] for i in range(n)) == 1)
Pattern 4 - Resource Allocation
Allocate resources with upper and lower bounds:
# Number of resources to allocate
n = 3
resources = [model.add_variable(f"resource_{i}",
vtype=Vtype.INTEGER,
lower=10,
upper=100)
for i in range(n)]
# Total resource constraint
model.add_constraint(sum(resources) <= 250)
# Minimum allocation per resource
for r in resources:
model.add_constraint(r >= 10)
Working with Dictionaries
For more complex models, use dictionaries to organize variables:
# Product and location dimensions
products = ['A', 'B', 'C']
locations = ['NYC', 'LA', 'CHI']
# Create variables using dictionary
inventory = {}
for p in products:
for loc in locations:
var_name = f"inv_{p}_{loc}"
inventory[(p, loc)] = model.add_variable(var_name,
vtype=Vtype.INTEGER,
lower=0)
# Add constraints by product
for p in products:
total = sum(inventory[(p, loc)] for loc in locations)
model.add_constraint(total >= 100) # Minimum total inventory
# Add constraints by location
for loc in locations:
total = sum(inventory[(p, loc)] for p in products)
model.add_constraint(total <= 500) # Warehouse capacity
Building Expressions Incrementally
For large models, build expressions step by step:
# Initialize empty expression
total_cost = 0
# Add terms incrementally
n = 100
for i in range(n):
x = model.add_variable(f"x_{i}", vtype=Vtype.BINARY)
cost = 5 + i * 0.1 # Cost varies by item
total_cost = total_cost + cost * x
# Set as objective
model.objective = total_cost
Quadratic Models
LunaModel supports quadratic objectives and constraints:
model = Model()
x = model.add_variable("x", vtype=Vtype.REAL, lower=-10, upper=10)
y = model.add_variable("y", vtype=Vtype.REAL, lower=-10, upper=10)
# Quadratic objective: minimize x^2 + y^2
model.objective = x*x + y*y
# Quadratic constraint: x^2 + y^2 <= 25 (circle)
model.add_constraint(x*x + y*y <= 25)
# Linear constraint: x + y >= 2
model.add_constraint(x + y >= 2)
Model Inspection
After building a model, inspect its properties:
# Basic statistics
print(f"Variables: {model.num_variables}")
print(f"Constraints: {model.num_constraints}")
print(f"Objective sense: {model.sense}")
# Iterate over variables
for var in model.variables():
print(f"{var.name}: {var.vtype}, bounds={var.bounds}")
# Iterate over constraints
for i, constraint in enumerate(model.constraints):
print(f"Constraint {i}: {constraint}")
# View objective
print(f"Objective: {model.objective}")
Model Validation
Check for common modeling errors:
# Check for variables without bounds
for var in model.variables():
if var.vtype == Vtype.INTEGER or var.vtype == Vtype.REAL:
lower, upper = var.bounds
if lower is None or upper is None:
print(f"Warning: {var.name} has unbounded range")
# Check for empty objective
if model.objective is None:
print("Warning: No objective set")
# Check for infeasible constraints (simple checks)
# e.g., x <= 5 and x >= 10
Example: Production Planning
Let's solve a complete production planning problem:
from luna_model import Model, Vtype, Sense
model = Model()
# Products to manufacture
products = ['Widget', 'Gadget', 'Doohickey']
# Production variables (units to produce)
production = {}
for p in products:
production[p] = model.add_variable(
f"produce_{p}",
vtype=Vtype.INTEGER,
lower=0,
upper=1000
)
# Resource requirements (hours per unit)
labor = {'Widget': 2, 'Gadget': 3, 'Doohickey': 1}
machine = {'Widget': 1, 'Gadget': 2, 'Doohickey': 1}
# Resource availability (hours)
labor_available = 500
machine_available = 400
# Resource constraints
model.add_constraint(
sum(labor[p] * production[p] for p in products) <= labor_available
)
model.add_constraint(
sum(machine[p] * production[p] for p in products) <= machine_available
)
# Demand constraints (minimum production)
demand = {'Widget': 50, 'Gadget': 30, 'Doohickey': 40}
for p in products:
model.add_constraint(production[p] >= demand[p])
# Profit per unit
profit = {'Widget': 12, 'Gadget': 20, 'Doohickey': 8}
# Maximize profit
model.set_objective(
sum(profit[p] * production[p] for p in products),
sense=Sense.MAX
)
print(model)
Tips for Effective Modeling
- Start simple: Build a minimal model first, then add complexity
- Use meaningful names:
num_workersis better thanx1 - Document constraints: Add comments explaining business rules
- Organize variables: Use lists or dictionaries for related variables
- Validate inputs: Check bounds and parameter values make sense
- Test with small instances: Debug with 3-5 variables before scaling up
Common Pitfalls
Pitfall 1 - Unbounded Variables
Pitfall 2 - Wrong Variable Type
Next Steps
- Variable Types - Learn about different variable types
- Expressions - Master building complex expressions
- Constraints - Explore advanced constraint patterns
- Solutions - Work with solution objects