Archived LunaModel 0.5.9 documentation
You are reading the old LunaModel 0.5.9 documentation.
The regular documentation describes LunaModel >=0.6.0; use the
latest documentation unless you explicitly need the old release.
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