Linear and Quadratic Programming
Page 704 established what convexity buys. This page spends it. Two families cover an enormous fraction of what actually gets solved:
- Linear programs — linear objective, linear constraints. The book calls them “one of the most widely used approaches in industry”.
- Quadratic programs — convex quadratic objective, linear constraints. Chapter 12’s support vector machine is one.
Both are convex, so strong duality holds and the dual is worth deriving. The derivations follow the same three steps each time, and the single place they diverge explains why their duals look so different.
What you’ll learn
Section titled “What you’ll learn”- Equation 7.39 (the LP) and Equation 7.45 (the QP), and what makes each convex.
- The three-step recipe for a Lagrangian dual: write it, collect the terms in , set the derivative in to zero.
- Why that third step produces a constraint for the LP (Equation 7.42) and a substitution for the QP (Equation 7.50) — the whole reason Equations 7.43 and 7.52 have different shapes.
- Example 7.5 solved exactly: , objective , and .
- Why an LP optimum is always a vertex, and the consequence: rotating the objective makes the answer jump. Measured — all vertices are optimal for some direction, with switches over sampled directions.
- Equation 7.51 checked against a direct evaluation of the Lagrangian: agreement to over random .
- How to decide whether to solve the primal or the dual, from against .
Intuition: a slab, and a bowl in a box
Section titled “Intuition: a slab, and a bowl in a box”A linear program is a flat sheet tilted over a polygon. There is no bottom of a bowl — the sheet just tilts — so the lowest point is wherever the polygon’s edge stops you. That is always a corner, and you can find it by checking every corner. There is no gradient information to follow, because the gradient of a linear function is the same vector everywhere.
A quadratic program is a bowl in a box. If the bowl’s bottom is inside the box you are done, and the constraints were irrelevant. If it is outside, the answer is pressed against a wall, and the wall pushes back with exactly enough force to hold it — which is the of page 703.
flowchart TD S1["step 1: write the Lagrangian
L = f + lambda-transpose (Ax - b)"] S1 --> S2["step 2: collect the terms in x"] S2 --> LP["LP: L = (c + A-transpose lambda)-transpose x - lambda-transpose b
x appears LINEARLY"] S2 --> QP["QP: L = half x-transpose Q x + (c + A-transpose lambda)-transpose x - lambda-transpose b
x appears QUADRATICALLY"] LP --> LP3["step 3: d/dx has no x in it,
so Eq 7.42 is a CONSTRAINT on lambda"] QP --> QP3["step 3: d/dx can be solved for x,
so Eq 7.50 SUBSTITUTES back"] LP3 --> LPD["Eq 7.43: the dual is another LP
m variables, d equality constraints"] QP3 --> QPD["Eq 7.52: the dual is a concave quadratic
m variables, lambda >= 0 only"] LPD --> Z["both gaps are zero:
convex, so strong duality"] QPD --> Z
§7.3.1 Linear Programming
Section titled “§7.3.1 Linear Programming”The problem
Section titled “The problem”with and : variables and linear constraints. Every is affine and therefore convex, and a linear objective is convex too (its Hessian is the zero matrix, which is positive semidefinite), so this is a convex problem by Equation 7.38.
The dual, in three steps
Section titled “The dual, in three steps”Step 1. The Lagrangian, with , :
Step 2. Collect the terms containing :
Step 3. Differentiate in and set to zero:
Here is the crucial observation. That equation contains no . You cannot solve it for and substitute back — there is nothing to substitute. Instead read it as a condition on : unless , the Lagrangian is a nonconstant linear function of and its minimum over is . So wherever the dual is finite, and the dual problem is
This is another linear program — but with variables and equality constraints, exactly the transpose of the primal’s shape. The book’s practical note: choose whichever is smaller. Recall is the number of variables and the number of constraints.
§7.3.2 Quadratic Programming
Section titled “§7.3.2 Quadratic Programming”The problem
Section titled “The problem”with square, symmetric and positive definite. That last condition is what makes the objective convex — its Hessian is itself — and it is also what makes exist, which the derivation needs.
The dual, same three steps
Section titled “The dual, same three steps”Step 1 and 2:
Step 3. Differentiate in and set to zero:
Now the is there, because the objective was quadratic. Assuming is invertible,
so we can substitute back. Writing and using :
and the dual problem is
A concave quadratic in with only the sign constraint — no equalities. Chapter 12 applies exactly this, and the becomes the kernel matrix.
Worked example by hand
Section titled “Worked example by hand”Example 7.5, the linear program
Section titled “Example 7.5, the linear program”, . Minimising means maximising , so we are pushing up and to the right — matching the book’s Figure 7.9 caption.
Step 1: find the vertices. A vertex in is where two constraint boundaries cross, so solve each of the pairs and keep the feasible ones. Five survive:
| vertex | active constraints | |
|---|---|---|
Step 2: read off the winner. The optimum is the first row. Solving rows and exactly:
Subtracting gives , so , and then . Check: ✓.
Step 3: the dual. Only the two active constraints can carry a nonzero multiplier (complementary slackness), so solve :
Subtracting: , so , and . Both positive, as required. So
The same . Duality gap zero, exactly as strong duality promised.
Example 7.6, the quadratic program
Section titled “Example 7.6, the quadratic program”Page 703 solved this one: with and . What is new here is that Equation 7.51 gives the dual objective in closed form, so it can be maximised directly rather than by solving the primal. With ,
and at we have , so
Expanding: . Differentiating and setting to zero: , so , and ✓.
See it move
Section titled “See it move”An LP is a combinatorial problem wearing continuous clothes. Rotate the objective and watch:
From scratch
Section titled “From scratch”import itertools
import numpy as np
# ---- Example 7.5, Equation 7.44 ------------------------------------------
c = np.array([-5.0, -3.0])
A = np.array([[2.0, 2.0], [2.0, -4.0], [-2.0, 1.0],
[0.0, -1.0], [0.0, 1.0]])
b = np.array([33.0, 8.0, 5.0, -1.0, 8.0])
def feasible(x):
return bool(np.all(A @ x <= b + 1e-9))
# An LP optimum is at a vertex, and a vertex is where two constraints meet.
# With only five constraints we can just enumerate all of them.
verts = []
for i, j in itertools.combinations(range(len(b)), 2):
M = A[[i, j]]
if abs(np.linalg.det(M)) < 1e-12:
continue # parallel boundaries never meet
v = np.linalg.solve(M, b[[i, j]])
if feasible(v) and not any(np.allclose(v, w, atol=1e-9) for w in verts):
verts.append(v)
verts = np.array(verts)
print(f"{len(verts)} feasible vertices, sorted by objective:")
for v in verts[np.argsort(verts @ c)]:
act = [i for i, q in enumerate(A @ v - b) if abs(q) < 1e-9]
print(f" [{v[0]:9.5f}, {v[1]:9.5f}] c^T x = {v @ c:10.5f} "
f"active {act}")
xstar = verts[int(np.argmin(verts @ c))]
print(f"\nx* = ({xstar[0]:.6f}, {xstar[1]:.6f}) = (37/3, 25/6)")
print(f"c^T x* = {xstar @ c:.6f} = -445/6")
# ---- Equation 7.43, the dual LP -----------------------------------------
# max -b^T lam s.t. c + A^T lam = 0, lam >= 0.
# Only the two active constraints can carry a nonzero multiplier, so solve
# the 2x2 system on those and check the rest are zero.
act = [i for i, q in enumerate(A @ xstar - b) if abs(q) < 1e-9]
lam = np.zeros(len(b))
lam[act] = np.linalg.solve(A[act].T, -c)
print(f"\nlambda* = {np.round(lam, 6).tolist()}")
print(f" as fractions: 13/6 = {13/6:.6f}, 1/3 = {1/3:.6f}")
print(f" stationarity c + A^T lambda = {np.round(c + A.T @ lam, 9).tolist()}")
print(f" all multipliers non-negative: {bool(np.all(lam >= -1e-12))}")
print(f" dual objective -b^T lambda = {-(b @ lam):.6f}")
print(f" primal objective c^T x* = {xstar @ c:.6f}")
print(f" duality gap = {abs(xstar @ c - (-(b @ lam))):.3e}")
print(f"\n primal: {A.shape[1]} variables, {A.shape[0]} constraints")
print(f" dual : {A.shape[0]} variables, {A.shape[1]} equality constraints")
# ---- the optimum is a corner, and it JUMPS ------------------------------
print("\nRotating the objective direction through 360 degrees:")
angles = np.linspace(0, 2 * np.pi, 3601)
winners = np.array([int(np.argmin(verts @ np.array([np.cos(a), np.sin(a)])))
for a in angles])
print(f" distinct vertices that are ever optimal: "
f"{len(set(winners.tolist()))} of {len(verts)}")
print(f" switches over 3600 sampled directions : "
f"{int((np.diff(winners) != 0).sum())}")
for k, v in enumerate(verts):
print(f" ({v[0]:8.4f}, {v[1]:7.4f}) is optimal over "
f"{360 * (winners == k).mean():5.1f} degrees")
# ---- Example 7.6, the QP, and Equation 7.51 ----------------------------
print("\n--- the quadratic program ---------------------------------------")
Q = np.array([[2.0, 1.0], [1.0, 4.0]])
cq = np.array([5.0, 3.0])
Aq = np.array([[1.0, 0.0], [-1.0, 0.0], [0.0, 1.0], [0.0, -1.0]])
bq = np.ones(4)
Qinv = np.linalg.inv(Q)
print(f"Q eigenvalues {np.linalg.eigvalsh(Q).round(6).tolist()} -> "
f"positive definite, so Q^-1 exists")
def primal(x):
return 0.5 * x @ Q @ x + cq @ x
def x_of_lam(l):
"""Equation 7.50."""
return -Qinv @ (cq + Aq.T @ l)
def dual(l):
"""Equation 7.51."""
v = cq + Aq.T @ l
return -0.5 * v @ Qinv @ v - l @ bq
# Equation 7.51 must equal the Lagrangian evaluated at Equation 7.50.
rng = np.random.default_rng(0)
worst = 0.0
for _ in range(20000):
l = rng.uniform(0, 4, 4)
x = x_of_lam(l)
worst = max(worst, abs((primal(x) + l @ (Aq @ x - bq)) - dual(l)))
print(f"max |L(x(lambda), lambda) - D(lambda)| over 20000 draws: {worst:.3e}")
ts = np.linspace(0.0, 6.0, 60001)
dv = np.array([dual(np.array([0.0, t, 0.0, 0.0])) for t in ts])
k = int(np.argmax(dv))
lam_q = np.array([0.0, ts[k], 0.0, 0.0])
xq = np.array([-1.0, -0.5])
print(f"\nlambda* = {np.round(lam_q, 6).tolist()}")
print(f"d* = {dual(lam_q):.9f} p* = {primal(xq):.9f} "
f"gap = {abs(primal(xq) - dual(lam_q)):.3e}")
print(f"x from Eq 7.50 = {np.round(x_of_lam(lam_q), 6).tolist()} "
f"matches x*: {np.allclose(x_of_lam(lam_q), xq, atol=1e-6)}")5 feasible vertices, sorted by objective:
[ 12.33333, 4.16667] c^T x = -74.16667 active [0, 1]
[ 8.50000, 8.00000] c^T x = -66.50000 active [0, 4]
[ 6.00000, 1.00000] c^T x = -33.00000 active [1, 3]
[ 1.50000, 8.00000] c^T x = -31.50000 active [2, 4]
[ -2.00000, 1.00000] c^T x = 7.00000 active [2, 3]
x* = (12.333333, 4.166667) = (37/3, 25/6)
c^T x* = -74.166667 = -445/6
lambda* = [2.166667, 0.333333, 0.0, 0.0, 0.0]
as fractions: 13/6 = 2.166667, 1/3 = 0.333333
stationarity c + A^T lambda = [0.0, 0.0]
all multipliers non-negative: True
dual objective -b^T lambda = -74.166667
primal objective c^T x* = -74.166667
duality gap = 1.421e-14
primal: 2 variables, 5 constraints
dual : 5 variables, 2 equality constraints
Rotating the objective direction through 360 degrees:
distinct vertices that are ever optimal: 5 of 5
switches over 3600 sampled directions : 5
( 12.3333, 4.1667) is optimal over 108.5 degrees
( 8.5000, 8.0000) is optimal over 45.0 degrees
( 6.0000, 1.0000) is optimal over 26.5 degrees
( -2.0000, 1.0000) is optimal over 116.7 degrees
( 1.5000, 8.0000) is optimal over 63.4 degrees
--- the quadratic program ---------------------------------------
Q eigenvalues [1.585786, 4.414214] -> positive definite, so Q^-1 exists
max |L(x(lambda), lambda) - D(lambda)| over 20000 draws: 1.776e-14
lambda* = [0.0, 2.5, 0.0, 0.0]
d* = -4.500000000 p* = -4.500000000 gap = 0.000e+00
x from Eq 7.50 = [-1.0, -0.5] matches x*: TrueOn real data
Section titled “On real data”Reading the plot
Section titled “Reading the plot”The first figure removes all the guesswork from Example 7.5. The five coloured lines are the constraint boundaries, the shaded pentagon is where all five hold, and the dotted lines are contours of — straight and parallel, because the objective is linear. The table on the right evaluates every vertex, and the point of listing all five is that the objective ranges from to across them. Picking the right corner is the entire problem; there is no gradient step that gets you there.
The active-constraint column is worth reading closely. Every vertex has exactly two active constraints, and there are exactly two variables. That is not a coincidence: a point in is pinned by independent equalities, so a vertex needs active constraints. Fewer and you are on an edge or a face and can still move; more and the constraints are degenerate. This is the fact that turns an LP into a finite search: there are at most candidate vertices, here , of which five turned out feasible.
The second figure explains why no amount of calculus helps. The left panel colours the boundary by objective value, and along each edge the colour changes linearly — because restricted to a line segment is an affine function of position. An affine function on an interval is monotone, so its minimum is at an endpoint. Walk the whole boundary and every local minimum is a corner. There is nowhere else for the answer to be.
Say the same thing with derivatives: , a constant nonzero vector. There is no stationary point anywhere. Everything page 701 built assumed you were looking for a place where the gradient vanishes, and here no such place exists. All the information lives in which constraints are active, which is a discrete question — and that is the structural reason simplex-type methods walk from vertex to vertex instead of following a slope.
The right panel shows the price of that discreteness. Sweeping the objective direction through , all five vertices are optimal for some range of directions, and there are exactly five switches. Each vertex owns a contiguous arc — for the winner at , only for — and the arcs sum to . Between arcs the answer does not drift; it teleports. So an arbitrarily small change in can move the solution to a completely different point, which matters whenever comes from estimated data.
The third figure is the payoff of the three-step recipe. The left panel puts the LP’s primal and dual side by side, and the shapes are transposed: variables with constraints becomes variables with equality constraints. Both reach , gap . For this problem the primal is the smaller one, so the book’s advice — solve whichever is smaller — says solve the primal. The advice earns its keep in the other direction: an SVM with training points and features has a dual with variables and a primal with , and Chapter 12 nonetheless solves the dual, because the dual is where the kernel trick lives. “Smaller” is a heuristic, not a rule.
The right panel is Equation 7.51 doing real work. It is a closed-form dual objective — no inner minimisation left to perform — and the curve is a concave parabola in whose peak sits exactly on . The verification in the corner is the one I would not have wanted to skip: substituting Equation 7.50 back into the Lagrangian must reproduce Equation 7.51 algebraically, and over random the largest discrepancy was — floating-point noise. The derivation is right.
Pitfalls
Section titled “Pitfalls”Compare
Section titled “Compare”| linear program, Eq 7.39 | quadratic program, Eq 7.45 | |
|---|---|---|
| objective | ||
| Hessian | , positive definite | |
| contours | parallel straight lines | concentric ellipses |
| stationary point of the objective | none | exactly one, at |
| where the optimum lives | always a vertex | vertex, edge, or interior |
| step 3 of the dual recipe | Eq 7.42, a constraint on | Eq 7.50, a substitution for |
| the dual | another LP, Eq 7.43 | concave quadratic, Eq 7.52 |
| dual constraints | and | only |
| measured gap | ||
| shows up in | resource allocation, transport, problems | SVM (Ch 12), portfolio choice, MPC |
-
Why is the optimum of a linear program always at a vertex?
An affine function on an interval attains its minimum at an endpoint. Walk the boundary and every local minimum is a corner. Saying it with derivatives: the gradient of c-transpose x is the constant vector c, so there is no stationary point anywhere — the whole answer is in which constraints are active, which is a discrete question.
pch.quizShowAnswer
B — Because the objective restricted to any edge is affine in position, hence monotone, so it has no interior minimum — An affine function on an interval attains its minimum at an endpoint. Walk the boundary and every local minimum is a corner. Saying it with derivatives: the gradient of c-transpose x is the constant vector c, so there is no stationary point anywhere — the whole answer is in which constraints are active, which is a discrete question.
-
In deriving the dual, step 3 sets the derivative in x to zero. What is different between the LP and the QP there?
c + A-transpose lambda = 0 has no x in it, so there is nothing to substitute — it becomes Equation 7.43's equality constraint, and the dual is another LP. Qx + (c + A-transpose lambda) = 0 does contain x, so Equation 7.50 solves for it and substituting gives the concave quadratic of Equation 7.51. That single step is the entire difference.
pch.quizShowAnswer
B — For the LP that equation contains no x, so it becomes a constraint on lambda; for the QP it can be solved for x and substituted back — c + A-transpose lambda = 0 has no x in it, so there is nothing to substitute — it becomes Equation 7.43's equality constraint, and the dual is another LP. Qx + (c + A-transpose lambda) = 0 does contain x, so Equation 7.50 solves for it and substituting gives the concave quadratic of Equation 7.51. That single step is the entire difference.
-
Example 7.5 has 2 variables and 5 constraints. How many constraints are active at each vertex?
A point in d dimensions is pinned by d independent equalities. Fewer active constraints and you are on an edge with room to move; more and the constraints are degenerate. Measured, all five vertices had exactly two active — which is what bounds the search to at most 5-choose-2 = 10 candidates.
pch.quizShowAnswer
B — Exactly two, matching the number of variables — A point in d dimensions is pinned by d independent equalities. Fewer active constraints and you are on an edge with room to move; more and the constraints are degenerate. Measured, all five vertices had exactly two active — which is what bounds the search to at most 5-choose-2 = 10 candidates.
-
What does rotating an LP's objective direction do to the optimal point?
Each vertex is optimal over a contiguous arc of directions — from 26.5 degrees for one to 108.5 for another, summing to 360 — and at an arc boundary the answer teleports. So a small change in an estimated c can move the solution to a completely different point, which is a real risk when c comes from data.
pch.quizShowAnswer
B — Makes it jump between vertices: measured, all 5 vertices win over contiguous arcs with 5 abrupt switches in 360 degrees — Each vertex is optimal over a contiguous arc of directions — from 26.5 degrees for one to 108.5 for another, summing to 360 — and at an arc boundary the answer teleports. So a small change in an estimated c can move the solution to a completely different point, which is a real risk when c comes from data.
-
Equation 7.50 writes x = -Q inverse times (c + A-transpose lambda). When is that step invalid?
Convexity only needs Q positive semidefinite; the closed-form dual needs it strictly positive definite. Example 7.6's eigenvalues are 1.585786 and 4.414214, both positive, which is what licenses the derivation — so check the eigenvalues before reaching for Equation 7.51.
pch.quizShowAnswer
B — When Q is positive semidefinite but singular: the objective is still convex, but Q inverse does not exist and the inner minimisation has a flat direction — Convexity only needs Q positive semidefinite; the closed-form dual needs it strictly positive definite. Example 7.6's eigenvalues are 1.585786 and 4.414214, both positive, which is what licenses the derivation — so check the eigenvalues before reaching for Equation 7.51.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Exercise 1 – Enumerate the vertices
Section titled “Exercise 1 – Enumerate the vertices”Exercise 2 – A vertex has as many active constraints as variables
Section titled “Exercise 2 – A vertex has as many active constraints as variables”Exercise 3 – The dual linear program
Section titled “Exercise 3 – The dual linear program”Exercise 4 – Rotate the objective and the answer jumps
Section titled “Exercise 4 – Rotate the objective and the answer jumps”Exercise 5 – Equation 7.51 is Equation 7.50 substituted back
Section titled “Exercise 5 – Equation 7.51 is Equation 7.50 substituted back”Recall card
Section titled “Recall card”- A linear program, Eq 7.39: minimise c-transpose x subject to Ax at most b. d variables, m constraints. Convex, because a linear objective has a zero Hessian and affine constraints are convex.
- A quadratic program, Eq 7.45: minimise half x-transpose Q x plus c-transpose x subject to Ax at most b, with Q symmetric POSITIVE DEFINITE. That is what makes the objective convex and Q invertible.
- One recipe, three steps: write the Lagrangian, collect the terms in x, set the derivative in x to zero. Both duals come out of the same procedure.
- The two families part at step 3. For the LP, c + A-transpose lambda = 0 contains no x, so it becomes a CONSTRAINT on lambda and the dual is another LP (Eq 7.43). For the QP, Qx + (c + A-transpose lambda) = 0 does contain x, so Eq 7.50 solves for it and substituting gives a concave quadratic (Eq 7.51 and 7.52).
- The LP dual has the transposed shape: m variables and d equality constraints. The book’s advice is to solve whichever of m and d is smaller — a default, not a rule, since Chapter 12 solves the larger SVM dual for structural reasons.
- An LP optimum is always a vertex, because the objective along any edge is affine and therefore monotone. Equivalently: the gradient of a linear objective is the constant vector c, so there is NO stationary point and no gradient method applies.
- A vertex in d dimensions has exactly d active constraints. Measured on Example 7.5: all five vertices had exactly two, with two variables. That bounds the search to at most m-choose-d candidates.
- Example 7.5 exactly: five feasible vertices out of ten intersections, objective from -74.167 to +7. x-star = (37/3, 25/6), value -445/6 = -74.166667, lambda-star = (13/6, 1/3, 0, 0, 0), gap 1.4e-14.
- The LP answer JUMPS. Rotating the objective through 360 degrees: all five vertices win over contiguous arcs (from 26.5 to 116.7 degrees), with exactly five switches. A small change in an estimated c can relocate the solution entirely.
- Eq 7.51 verified: substituting Eq 7.50 into the Lagrangian reproduces the closed-form dual to 1.8e-14 over 20000 random multipliers. Example 7.6’s Q eigenvalues are 1.585786 and 4.414214.
- Positive SEMIdefinite is not enough for Eq 7.50. The objective stays convex, but Q is singular, the inner minimisation has a flat direction, and the closed form is invalid. Check eigenvalues first.
- A zero multiplier means “not binding here”, not “removable”. Rotate the objective and different constraints activate, and their prices become nonzero.
Next: duality without constraints, and the transform that turns a function into a function of its own slopes. Legendre-Fenchel Transform and Convex Conjugate
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading