Linear Programming in Python (PuLP/OR-Tools): Step-by-Step

Last updated: β€’ Sources β€’ Methodology

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: 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
Repro tip: export your environment to a requirements file:
pip freeze > requirements.txt
If 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
If your model requires integer/binary decisions, it is not an LP anymore. Use ILP/MILP and expect branch-and-bound.

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.

Cross-check by entering the same LP into the LP calculator.
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.

If you see multiple solutions with the same objective value, your LP likely has alternate optimaβ€”see Multiple Optimal Solutions in LP.

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.

Sources
  1. PuLP Documentation (COIN-OR)
  2. Google OR-Tools β€” Linear Programming documentation
  3. GNU GLPK β€” LP/MIP solver documentation (background solver reference)
  4. Wikipedia β€” Linear programming (definitions and terminology)