Integer Programming

When variables must be integers, the LP relaxation is a free lower bound — and the rest of the story is what to do with it.

What changes when variables must be integers

An integer linear program (ILP) is a linear program with the extra requirement that some or all variables take integer values:

$$\begin{aligned} \min \;& \mathbf{c}^\top \mathbf{x} \\ \text{s.t.} \;& A \mathbf{x} \le \mathbf{b}, \\ & \mathbf{x} \ge \mathbf{0}, \quad \mathbf{x} \in \mathbb{Z}^n. \end{aligned}$$

The feasible set is no longer a polytope but a finite collection of lattice points inside one. Even though the objective is linear, this problem is NP-hard in general. There is no analogue of "vertex of the simplex" you can walk to optimality in polynomial time.

The LP relaxation gives a free lower bound

Drop the integrality constraint $\mathbf{x} \in \mathbb{Z}^n$. The remaining problem is an LP — solvable in polynomial time:

$$z_{\text{LP}} = \min \{\mathbf{c}^\top \mathbf{x} : A\mathbf{x} \le \mathbf{b},\, \mathbf{x} \ge \mathbf{0}\}.$$

Because the LP feasible set contains the integer feasible set, we always have

$$\boxed{\;z_{\text{LP}} \;\le\; z_{\text{IP}}\;}$$

for a minimization problem. The LP optimum is a lower bound for the IP optimum. (For maximization, the inequality flips and the LP is an upper bound — same idea.)

This is the engine of integer programming. A bound by itself doesn't solve the problem, but it tells you how good your current best integer solution is, and lets you prove parts of the search space cannot contain anything better.

Three things can happen at the root LP:

Branch and bound

Pick a variable $x_j$ that is fractional in the LP optimum, value $x_j^\star = \tilde v$. Split the problem into two children:

Every integer feasible solution satisfies one of these two constraints, so we lose no integer solutions. We have replaced one hard problem with two slightly more constrained children — and we keep solving LPs.

Maintain an incumbent $z^\star$, the best integer solution found so far ($+\infty$ initially). At each open node:

Pruning ruleCondition (min problem)Why we can stop
by integrality LP optimum is integer Update incumbent if better; the subtree is fully explored.
by infeasibility LP is infeasible The IP under the same constraints is also infeasible.
by bound $z_{\text{LP}}^{\text{node}} \ge z^\star$ Even the LP relaxation can't beat the incumbent. No integer solution in this subtree can either.

Branch-and-bound is a tree search, but most of the tree is never built — pruning by bound shaves off entire branches.

Branch-and-bound on a 2-variable maximization

$\max\; 5x_1 + 4x_2$  s.t. $6x_1 + 4x_2 \le 24,\;\, x_1 + 2x_2 \le 6,\;\, x_1, x_2 \ge 0$ integer. (LP gives an upper bound for max.)

Scaling up: 10 binary variables

The 2-variable problem is small enough to draw, but it doesn't show what makes B&B a real algorithm — the pruning. With $n$ binary variables there are $2^n$ feasible candidates ($2^{10} = 1024$ here), and a brute-force search would enumerate all of them. B&B explores a tiny fraction.

The next demo runs B&B on a 0-1 knapsack problem:

$$\max \sum_{i=1}^{10} v_i x_i \quad \text{s.t.} \quad \sum_{i=1}^{10} w_i x_i \le W, \quad x_i \in \{0, 1\}.$$

The LP relaxation of a knapsack is solved in closed form: sort items by ratio $v_i / w_i$ descending, take whole items greedily until the next one wouldn't fit, take a fraction of that one to fill the bag exactly. Exactly one variable comes out fractional (the "split item"), and that's the variable to branch on.

The widget below uses best-first node selection: at every step it expands the open node with the largest LP bound. Watch what happens: many subtrees are pruned because their LP bound — even before rounding — cannot beat the incumbent.

Knapsack branch-and-bound (10 binary variables)

Gomory cuts: tightening the relaxation

Branching is one way to make progress when the LP optimum is fractional. Another is to add a constraint — a "cutting plane" — that

  1. is satisfied by every integer feasible solution, but
  2. cuts off the current fractional LP optimum.

Geometrically, we are nudging the LP feasible region toward the convex hull of integer feasible points (the "integer hull"), without ever cutting off a single integer point.

The Chvátal–Gomory procedure takes a non-negative combination of the rows of $A\mathbf{x} \le \mathbf{b}$ and rounds down. With $\mathbf{x} \ge \mathbf{0}$ integer and any $\mathbf{u} \ge \mathbf{0}$,

$$\mathbf{u}^\top A\, \mathbf{x} \le \mathbf{u}^\top \mathbf{b} \;\Rightarrow\; \lfloor \mathbf{u}^\top A \rfloor\, \mathbf{x} \le \lfloor \mathbf{u}^\top \mathbf{b} \rfloor$$

where the floor on the LHS is component-wise. Repeating this construction generates the Chvátal–Gomory closure, and finitely many rounds reach the integer hull. In practice solvers read off cuts directly from the simplex tableau (Gomory's fractional cut).

Gomory cut demo

$\max\; x_1 + x_2$  s.t. $5x_1 + 4x_2 \le 19,\;\, -2x_1 + 2x_2 \le 1,\;\, x_1, x_2 \ge 0$ integer. The LP optimum is fractional. Add cuts to close the gap.

Branch-and-cut: how real solvers work

Modern MIP solvers (CPLEX, Gurobi, HiGHS, SCIP) combine the two ideas: at every node of the B&B tree, run a few rounds of cutting planes to tighten the LP before deciding to branch. This is called branch-and-cut. The cuts come from a zoo of families — Gomory mixed-integer cuts, lift-and-project, mixed-integer rounding, clique cuts, flow cover cuts — and the solver decides which ones are worth adding.

Other practical ingredients:

Want to read more? Wolsey, Integer Programming (1998 / 2nd ed. 2020) is the standard reference. Conforti, Cornuéjols, Zambelli, Integer Programming (2014) is more modern. For a hands-on tour, Achterberg's SCIP papers and Gurobi's blog posts are very readable.