Linear Programming in Python (PuLP/OR-Tools): Model, Solve, Read Status, and Debug with Worked Examples
This page shows a practical workflow for linear programming in Python using PuLP and Google OR-Tools: how to define decision variables, add constraints, solve, interpret solver statuses, and troubleshoot infeasible or unbounded models.
Want a fast cross-check of results? Solve the same model in the Linear Programming calculator. If youβre new to LP concepts, start with Linear Programming (LP) β Beginner Guide.
On this page
- Quick takeaway (what youβll build)
- Install PuLP / OR-Tools (reproducible setup)
- Modeling patterns: variables, objective, constraints
- How to interpret solver status (optimal/infeasible/unbounded)
- Questions people ask (Python LP)
- Worked examples (fully solved)
- Verify in the LP calculator + Excel
- Troubleshooting table (common Python errors)
- Glossary
Quick takeaway: In linear programming in Python, you (1) define decision variables (with bounds), (2) add a linear objective, (3) add linear constraints, (4) solve with a backend LP solver, then (5) read the status and solution values. If the solver returns infeasible or unbounded, the fastest fix is usually modeling: check signs, bounds, and units using Unbounded vs Infeasible LP (Fix Checklist) and Upper Bounds in Linear Programming.
Install PuLP / OR-Tools (reproducible setup)
Setup
Recommended: virtual environment + pinned packages
python -m venv .venv # Windows: .venv\Scripts\activate # macOS/Linux: source .venv/bin/activate pip install pulp ortools
pip freeze > requirements.txtIf you also want reproducible model exchange across tools, exporting a structured model representation helps; see what is model JSON (CalcTypes format).
Modeling patterns: variables, objective, constraints
Core pattern
Every LP model in code has the same building blocks
| Concept | What it is | Typical mistake |
|---|---|---|
| Decision variables | The x values you choose |
Missing bounds β unbounded model |
| Objective | Profit/cost to maximize/minimize | Wrong direction (max vs min) |
| Constraints | Capacity/demand/budget requirements | Flipped inequality sign (β€ vs β₯) |
| Status | Optimal / infeasible / unbounded | Reading variable values without checking status |
If youβre converting constraints for simplex theory (slack/surplus/artificial), see Slack / Surplus / Artificial Variables.
How to interpret solver status (optimal/infeasible/unbounded)
Status
Always check status before trusting numbers
Your code can still print numbers even when the model is infeasible or unbounded (often leftover values from presolve/initialization). Treat status as the gatekeeper:
| Status | Meaning | What to do next |
|---|---|---|
| Optimal | Feasible solution found and objective is best possible | Use the solution; optionally interpret duals (see primalβdual relationship) |
| Infeasible | No solution satisfies all constraints | Use fix checklist; check sign/units and missing flexibility |
| Unbounded | Objective can improve forever while staying feasible | Add realistic upper bounds or missing limiting constraints |
Questions people ask about linear programming in Python (PAA)
People ask
How do you do linear programming in Python?
You define decision variables with bounds, write a linear objective, add linear constraints, and solve using a library like PuLP or OR-Tools. Then you read the solver status and extract variable values and the objective value.
People ask
Is PuLP or OR-Tools better for linear programming in Python?
PuLP is lightweight and easy for textbook LP/ILP models. OR-Tools is a broader optimization toolkit with multiple solver backends. For pure LPs, both are fine; choose based on your preferred API and deployment needs.
People ask
Why does my linear program in Python return βinfeasibleβ?
Usually the constraints contradict each other, an inequality sign is flipped, or you accidentally imposed impossible bounds. Use Unbounded vs Infeasible LP (Fix Checklist) and test simplified versions of the model.
People ask
Why does my linear program in Python return βunboundedβ?
Unbounded almost always means a variable can grow without limit while improving the objective. Add realistic bounds (upper bounds guide) or add the missing limiting constraint.
People ask
How do I verify my Python LP solution?
Solve the same model in a second tool: use the Linear Programming calculator or Excel Solver. If itβs a 2-variable LP, also verify by graphical method.
People ask
How do I get shadow prices (dual values) in Python LP?
Dual values depend on the solver backend and API you use. Conceptually, they come from the dual LP and interpret as shadow prices. For the meaning and how to use them, see PrimalβDual Relationship in LP.
Worked examples (fully solved)
Worked examples
Expand only the example you need
Each example includes the LP statement, Python code, expected optimum, and a calculator cross-check path.
Example 1 β Linear programming in Python with PuLP (maximize)
LP (maximize):
maximize z = 3x1 + 5x2
subject to 2x1 + x2 β€ 100
x1 + 3x2 β€ 90
x1, x2 β₯ 0
PuLP code
import pulp as pl
# Model
m = pl.LpProblem("ProductMix", pl.LpMaximize)
# Variables
x1 = pl.LpVariable("x1", lowBound=0)
x2 = pl.LpVariable("x2", lowBound=0)
# Objective
m += 3*x1 + 5*x2
# Constraints
m += 2*x1 + 1*x2 <= 100
m += 1*x1 + 3*x2 <= 90
# Solve
status = m.solve(pl.PULP_CBC_CMD(msg=False))
print("Status:", pl.LpStatus[status])
print("x1 =", pl.value(x1))
print("x2 =", pl.value(x2))
print("z =", pl.value(m.objective))
Expected solution (hand-check)
Intersection of binding constraints:
2x1 + x2 = 100 x1 + 3x2 = 90 β x2 = 16, x1 = 42
z* = 3(42) + 5(16) = 206
Optimal solution (Example 1): x1=42, x2=16, z*=206.
Example 2 β Linear programming in Python with OR-Tools (minimize)
LP (minimize):
minimize z = 3x1 + 2x2
subject to 2x1 + x2 β₯ 8
x1 + 2x2 β₯ 10
x1, x2 β₯ 0
OR-Tools code (Linear Solver API)
from ortools.linear_solver import pywraplp
solver = pywraplp.Solver.CreateSolver("GLOP") # LP solver
x1 = solver.NumVar(0.0, solver.infinity(), "x1")
x2 = solver.NumVar(0.0, solver.infinity(), "x2")
# Constraints
solver.Add(2*x1 + 1*x2 >= 8)
solver.Add(1*x1 + 2*x2 >= 10)
# Objective (min)
solver.Minimize(3*x1 + 2*x2)
status = solver.Solve()
print("Status:", status) # compare with solver.OPTIMAL etc.
print("x1 =", x1.solution_value())
print("x2 =", x2.solution_value())
print("z =", solver.Objective().Value())
Expected solution (hand-check)
2x1 + x2 = 8 x1 + 2x2 = 10 β x2 = 4, x1 = 2 z* = 3(2) + 2(4) = 14
Optimal solution (Example 2): x1=2, x2=4, z*=14.
Example 3 β Debug example: infeasible vs unbounded (what to change)
Case A: infeasible LP (contradictory constraints)
maximize z = x1
subject to x1 β€ 1
x1 β₯ 3
x1 β₯ 0
No value of x1 can satisfy x1 β€ 1 and x1 β₯ 3 simultaneously, so the model is infeasible.
Fix by revisiting the business rules (one of these constraints is wrong or needs slack).
Case B: unbounded LP (missing upper bound)
maximize z = x1 subject to x1 β₯ 0
Here x1 can increase without limit, so the objective has no finite maximum (unbounded).
Fix by adding a realistic capacity/budget bound, e.g. x1 β€ U or a resource constraint.
Use Upper Bounds in Linear Programming.
Debug rule: If the solver says infeasible, look for contradictions (upper bound < lower bound). If it says unbounded, look for a variable that can grow while improving the objective and has no limiting constraint/bound. Use Unbounded vs Infeasible LP (Fix Checklist).
Verify in the LP calculator + Excel
Verification
Best practice: confirm your Python model in a second tool
For small models, cross-check in the Linear Programming calculator or in Excel Solver. If your LP has 2 variables, also verify via graphical method.
Troubleshooting table (common Python errors)
Troubleshooting
Symptom β likely cause β fix
| What you observe | Likely cause | Fix |
|---|---|---|
| Solver returns a status but variables look nonsense | Reading solution without checking status | Check status first (Optimal/Infeasible/Unbounded) |
| Unbounded | Missing variable upper bounds or limiting constraints | Add realistic bounds (upper bounds guide) |
| Infeasible | Contradictory constraints, wrong inequality direction, unit mismatch | Use fix checklist; simplify model to isolate contradiction |
| PuLP canβt find/execute a solver | Solver backend missing or path issues | Start with default CBC via PULP_CBC_CMD; reinstall packages; test in a clean venv |
| OR-Tools status not OPTIMAL | Model infeasible/unbounded or wrong solver selected | Use GLOP for LP; add bounds; check constraints |
| You added integer/binary variables and performance drops | It is now ILP/MILP (not LP) | Use ILP/MILP and learn branch-and-bound |
Glossary
Glossary
- LP (linear program): optimize a linear objective subject to linear constraints.
- Decision variable: a model variable chosen by the solver (e.g.,
x1). - Constraint: a linear restriction (capacity, demand, budget).
- Objective: the linear expression you maximize or minimize.
- Backend solver: the numerical engine that actually solves the LP (PuLP/OR-Tools interface to it).
- Status: Optimal/Infeasible/Unbounded outcome code returned by the solver.
- Shadow price (dual value): dual interpretation of constraint value (see primalβdual guide).
Disclaimer
This article and the associated calculators are provided for educational and informational purposes only. Optimization outcomes depend on modeling assumptions and input data and may not reflect real-world constraints unless you encode them correctly. This is not legal, financial, operational, or safety advice. For high-stakes decisions, validate results against domain requirements and consult a qualified professional. For details, read our full disclaimer.