- Published on
Aspects of OR - Integer Programming (Part 3)
- Authors

- Name
- Till Heller
In Part 2 we saw how branch and bound systematically searches for integer solutions using LP bounds to prune the search tree. The effectiveness of this approach depends critically on the quality of the LP bounds. Loose bounds mean less pruning, bigger trees, and slower solves.
Cutting planes address this directly: they add new constraints (cuts) to the LP relaxation that remove fractional solutions without eliminating any integer-feasible point. Each cut tightens the LP relaxation, improving the bound. The combination of cutting planes with branch and bound is called branch-and-cut, and it is the algorithmic engine behind every modern IP solver.
The cutting plane idea
Consider an IP with feasible region and its LP relaxation with feasible region . Clearly .
A valid inequality (or cut) for is an inequality that is satisfied by every point in but violated by some points in — in particular, by the current fractional LP solution. Adding this cut to the LP shrinks without losing any integer points.
The ideal LP relaxation would be the convex hull of : the smallest convex set containing all integer-feasible points. If we could describe this convex hull explicitly, the LP relaxation would always give integer solutions. In general, the convex hull has exponentially many facets and cannot be written down. But we can iteratively add cuts that bring us closer to it.
Gomory cuts
The most classical cutting plane method is due to Ralph Gomory (1958). Gomory cuts are derived directly from the simplex tableau and can be applied to any IP.
Derivation
Suppose we have solved the LP relaxation with the simplex method and the optimal solution has a fractional basic variable with value . The corresponding row of the simplex tableau is:
where is the set of non-basic variables. Since all variables must be non-negative integers in the IP, we can derive the Gomory fractional cut:
where is the fractional part of , and is the fractional part of .
This inequality is valid for all integer solutions but violated by the current LP solution (where all non-basic variables are zero, giving , a contradiction since ).
Example
Consider a small IP:
The LP relaxation gives with value 22. This is already integer! In this case no cuts are needed.
Let us modify the problem slightly to create a fractional LP solution:
The LP optimal solution is , , with LP value .
From the simplex tableau row for , we can derive a Gomory cut. The fractional parts give us an inequality like (the exact form depends on the tableau). Adding this cut to the LP and resolving tightens the bound. After one or two rounds, the LP solution becomes integer.
Cover inequalities for knapsack
For specific problem structures, we can derive much stronger cuts than generic Gomory cuts. Cover inequalities are a prime example, designed for knapsack constraints.
Consider a binary knapsack constraint:
A cover is a subset of items whose total weight exceeds the capacity:
Since the items in a cover cannot all be selected simultaneously, we get the cover inequality:
This is a valid inequality for the IP but may be violated by the LP relaxation.
Example
Using the knapsack from Parts 1 and 2: weights , capacity 50.
The set with total weight is not a cover (we need strictly greater). But with total weight is a cover. The cover inequality is:
This is automatically satisfied here (we can take at most 2 items anyway), so it does not cut off the LP solution. In larger knapsack instances with many items, cover inequalities can be highly effective at tightening the LP relaxation.
Extended cover inequalities strengthen the basic cover by also considering items outside the cover that are heavier than the lightest item in the cover, yielding even tighter cuts.
The pure cutting plane algorithm
Gomory proposed the following algorithm:
- Solve the LP relaxation.
- If the solution is integer, stop — it is optimal for the IP.
- Otherwise, generate one or more cutting planes violated by the current LP solution.
- Add the cuts to the LP and go to Step 1.
This algorithm is guaranteed to converge to the IP optimum in finite steps (for pure IPs). However, pure cutting plane algorithms tend to be slow in practice: they may require many rounds of cuts, and numerical issues accumulate as the LP grows.
Branch-and-cut
The practical breakthrough came from combining cutting planes with branch and bound into branch-and-cut:
- At each node of the B&B tree, solve the LP relaxation.
- Before branching, try to generate cutting planes that are violated by the current LP solution.
- If effective cuts are found, add them to the LP and resolve. Repeat until no more useful cuts can be found.
- If the LP solution is still fractional, branch as in standard B&B.
This approach gets the best of both worlds: cuts tighten the LP bounds (leading to more pruning), and branching handles the remaining integrality gap.
Modern solvers implement this with sophisticated cut management:
- Cut generation at each node using multiple families of cuts (Gomory, cover, clique, flow cover, mixed-integer rounding, and many more).
- Cut selection to choose the most effective cuts and avoid adding too many (which would slow down the LP solves).
- Cut aging and removal of cuts that are no longer binding.
What modern solvers actually do
A solver like Gurobi or CPLEX performs dozens of operations beyond basic branch-and-cut. Here is a simplified overview of the solve process:
Presolve: Before any LP is solved, the solver simplifies the model. This includes fixing variables whose values are determined by the constraints, tightening variable bounds, removing redundant constraints, detecting infeasibility early, and reformulating parts of the model. Presolve alone can reduce a problem by 50% or more.
Root node processing: At the root node, the solver invests heavily in cutting planes. It generates multiple rounds of cuts from various families, keeping only the most effective ones. It also runs primal heuristics to find good feasible solutions quickly — these serve as the initial incumbent for pruning.
Tree search: The solver then explores the B&B tree, generating cuts at some (not all) nodes, running heuristics periodically, and using adaptive node and variable selection strategies. It monitors the MIP gap and adjusts its strategy dynamically.
Parallelism: Modern solvers exploit multiple CPU cores by exploring different subtrees in parallel (deterministic or opportunistic parallelism).
Seeing it in action with Gurobi
Gurobi logs provide insight into the branch-and-cut process. Let us solve a slightly larger knapsack to see cuts in action:
import gurobipy as gp
from gurobipy import GRB
import random
random.seed(42)
n = 20 # 20 items
weights = [random.randint(5, 50) for _ in range(n)]
values = [random.randint(10, 100) for _ in range(n)]
capacity = int(0.4 * sum(weights))
model = gp.Model('knapsack_20')
model.Params.OutputFlag = 1 # show solver log
x = model.addVars(n, vtype=GRB.BINARY, name='x')
model.setObjective(gp.quicksum(values[j] * x[j] for j in range(n)), GRB.MAXIMIZE)
model.addConstr(gp.quicksum(weights[j] * x[j] for j in range(n)) <= capacity, 'capacity')
model.optimize()
selected = [j for j in range(n) if x[j].X > 0.5]
print(f'\nSelected items: {selected}')
print(f'Total weight: {sum(weights[j] for j in selected)} / {capacity}')
print(f'Total value: {model.ObjVal:.0f}')
In the solver log, you will see lines like:
Cutting planes:
Cover: 3
GUB cover: 1
This shows that Gurobi added cover inequalities (and GUB cover inequalities, a generalization) to tighten the LP relaxation at the root node. For this small problem, the cuts combined with presolve may be sufficient to close the gap entirely at the root, requiring zero or very few branch-and-bound nodes.
Accessing cut information
Gurobi also allows you to add your own cuts via lazy constraints or user cuts. This is useful when you know problem-specific valid inequalities:
import gurobipy as gp
from gurobipy import GRB
def my_callback(model, where):
if where == GRB.Callback.MIPNODE:
status = model.cbGet(GRB.Callback.MIPNODE_STATUS)
if status == GRB.OPTIMAL:
x_val = model.cbGetNodeRel(x)
# Check for violated cuts and add them
# model.cbCut(expr <= rhs)
model = gp.Model('with_cuts')
# ... define variables and constraints ...
model.optimize(my_callback)
This callback mechanism is used in practice for problems like the TSP, where subtour elimination constraints are added on-the-fly as they become violated (there are exponentially many, so adding them all upfront is impractical).
Performance impact
To illustrate the impact of cuts, consider solving an IP in three modes:
- LP relaxation only — solve the LP, report the bound. Fast but loose.
- Branch and bound only — disable cuts in the solver. The tree may be very large.
- Branch-and-cut — the default. Cuts tighten bounds, leading to smaller trees.
import gurobipy as gp
from gurobipy import GRB
# ... define model as above ...
# Mode 2: B&B only (disable cuts)
model.Params.Cuts = 0
model.optimize()
print(f'Nodes explored (no cuts): {model.NodeCount}')
model.reset()
# Mode 3: Branch-and-cut (default)
model.Params.Cuts = -1 # automatic (default)
model.optimize()
print(f'Nodes explored (with cuts): {model.NodeCount}')
For many problems, enabling cuts reduces the number of B&B nodes by an order of magnitude or more.
Summary of the IP mini-series
Across the three parts, we covered:
- Part 1 — IP definitions, LP relaxation, why rounding fails, knapsack and facility location examples.
- Part 2 — Branch and bound: the tree search algorithm, pruning criteria, node and variable selection, worked knapsack example.
- Part 3 — Cutting planes (Gomory cuts, cover inequalities), branch-and-cut, and how modern solvers work.
Together with the LP mini-series, these six posts cover the core theory and algorithms of mathematical programming. This foundation supports everything that follows in the Aspects of OR series — from scheduling to vehicle routing to network design.