5.4.10 · D4Scientific Computing (Python)

Exercises — scipy.optimize — minimize, fsolve, curve_fit, linprog

2,473 words11 min readBack to topic

The four engines in one glance

Before we start, a picture of what problem each tool solves. Look at each panel: the little dot is the answer the tool hunts for.

Figure — scipy.optimize — minimize, fsolve, curve_fit, linprog

Level 1 — Recognition

L1·Q1 — Which tool?

For each task, name the single best scipy.optimize function.

(a) Find the smallest value of . (b) Find every where . (c) Choose so hugs 200 noisy sensor readings. (d) Maximize subject to , .

Recall Solution

(a) minimize — we want the smallest value of a scalar objective. (b) fsolve — rewrite as ; we want a root. (c) curve_fit — we are fitting a model to data (least squares). (d) linproglinear objective, linear constraints ⇒ the answer lives at a corner.

The tell-tale words: smallest → minimize, equals zero / solve → fsolve, fit data → curve_fit, linear + constraints → linprog.

L1·Q2 — Read the result object

After res = minimize(f, x0), which attribute holds the winning input, which holds the winning score, and which is the True/False flag that it worked?

Recall Solution
  • res.x ::: the solution vector (the winning input).
  • res.fun ::: the objective value (the winning score).
  • res.success ::: boolean flag — True if the solver believes it converged.

Level 2 — Application

L2·Q1 — Minimize a 1D quartic

Minimize starting from x0=2. What is and ?

Recall Solution

WHAT: we look for a stationary point where the slope . WHY those: setting the derivative to zero finds flat spots — candidates for minima. is a local max (a hilltop), and are the two valleys. Starting at x0=2 (right side), the iteration rolls down into the right valley .

from scipy.optimize import minimize
f = lambda x: x[0]**4 - 3*x[0]**2 + 2
res = minimize(f, x0=[2.0])
print(res.x)    # ≈ [1.2247]
print(res.fun)  # ≈ -0.25

Answer: , .

L2·Q2 — fsolve a nonlinear pair

Solve and from x0=[1,1].

Recall Solution

WHY move to one side: fsolve seeks where the returned vector is , so rewrite each equation as something = 0.

from scipy.optimize import fsolve
def F(v):
    x, y = v
    return [x**2 + y**2 - 25, x - y - 1]
print(fsolve(F, x0=[1,1]))   # ≈ [4., 3.]

By hand: , so . Roots (giving ) or (giving ). The seed [1,1] is nearer the first root. Answer: .

L2·Q3 — linprog a tiny plan

Maximize with , , .

Recall Solution

WHY negate: linprog only minimizes, so to maximize we minimize .

from scipy.optimize import linprog
c = [-5, -4]
A_ub = [[6,4],[1,2]]
b_ub = [24, 6]
res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=[(0,None),(0,None)])
print(res.x)      # ≈ [3., 1.5]
print(-res.fun)   # 21.0

Corner-hunting by hand: the two active constraints intersect where and . Multiply the second by 2: . Subtract from the first: , then . Objective . Answer: , profit .


Level 3 — Analysis

L3·Q1 — One Newton step by hand

fsolve is built on Newton's method: . For starting at , compute and by hand. What are they converging to?

Recall Solution

WHAT Newton does: it replaces the curve by its tangent line at and jumps to where that line crosses zero — a picture worth holding in mind. . It is racing toward . Notice how fast: already matches to three decimals. That doubling of correct digits per step is Newton's signature. See Newton's Method. Answer: , , converging to .

L3·Q2 — Why the seed decides the root

For the system in L2·Q2, predict which root fsolve returns from x0=[-5,-5], and explain geometrically.

Figure — scipy.optimize — minimize, fsolve, curve_fit, linprog
Recall Solution

WHY the seed matters: Newton-type solvers are local — they follow the tangent downhill from the seed and lock onto the nearest root, never scanning globally. The circle meets the line at two points: (upper-right) and (lower-left). The seed sits in the lower-left, closest to . Answer: fsolve returns . Look at the figure: each seed's basin of attraction is the side of the line it starts on.

L3·Q3 — Least-squares line by the normal equations

Fit to the three points . Minimize analytically. Give .

Recall Solution

WHY set derivatives to zero: is a smooth bowl in ; its lowest point has both partial slopes zero. and give the normal equations: Numbers: , , , , . Subtract: ; then . This is exactly what Least Squares Regression and NumPy polyfit compute in closed form — no iteration needed because the model is linear. Answer: , .


Level 4 — Synthesis

L4·Q1 — Same problem, two tools

Find the minimum of twice: once with minimize, once by handing to fsolve. Show both give the same .

Recall Solution

The unifying idea: minimizing = finding a root of . Two APIs, one skeleton. . Root of is ; that is also the valley bottom, where .

from scipy.optimize import minimize, fsolve
f  = lambda x: (x[0]-4)**2 + 1
df = lambda x: [2*(x[0]-4)]
print(minimize(f, x0=[0]).x)   # ≈ [4.]
print(fsolve(df, x0=[0]))      # ≈ [4.]

Answer: both return ; . The Hessian confirms it is a minimum (see Gradient and Hessian).

L4·Q2 — Constrained by hand, checked by linprog

A bakery makes cakes () and pies (), profit \3$2x_1+x_2\le 1002x_1+x_2\le 150\ge 0$. Find the best mix and profit.

Recall Solution

WHY corners: a linear objective never flattens inside the region — its gradient is constant — so you slide along the walls to a vertex (see Linear Programming Simplex, Convex Optimization). Candidate corners of the feasible polygon:

  • : profit .
  • (oven wall, ): profit .
  • (flour wall, ): profit .
  • Intersection of oven & flour: , . Subtract ⇒ , . Profit . The intersection corner wins.
from scipy.optimize import linprog
res = linprog([-3,-2], A_ub=[[1,1],[2,1]], b_ub=[100,150],
              bounds=[(0,None),(0,None)])
print(res.x)     # ≈ [50., 50.]
print(-res.fun)  # 250.0

Answer: 50 cakes, 50 pies, profit \250$.


Level 5 — Mastery

L5·Q1 — Exponential fit, error bars

You fit with curve_fit and get pcov = [[0.0016, 0], [0, 0.0009]]. Report the uncertainty on and . What does a large off-diagonal entry mean?

Recall Solution

WHY the square root: pcov is the covariance matrix; its diagonal holds variances . The uncertainty (standard deviation) is the square root.

import numpy as np
pcov = [[0.0016,0],[0,0.0009]]
print(np.sqrt(np.diag(pcov)))   # [0.04 0.03]

A large off-diagonal entry means and are correlated — the fit can't pin them down independently (raising while lowering gives a similar curve). Here the off-diagonals are , so they are independent. Answer: , .

L5·Q2 — Where Newton stalls

Explain what happens when fsolve (Newton at heart) tries from . Compute two steps and describe the failure/slowdown.

Recall Solution

The subtlety: is a double root ( and both vanish there). Newton's fast doubling of digits assumes a simple root; at a double root it crawls, roughly halving the error each step (linear, not quadratic). . Update . It keeps halving toward — it does converge, but slowly, and fsolve may report near-success with a residual that shrinks lazily. Answer: , ; convergence is merely linear (halving) because of the double root.

L5·Q3 — Diagnose a bad fit

Someone runs curve_fit(model, x, y) on a nonlinear model with no p0, and gets nonsense (or a RuntimeError). State the cause and the one-line fix.

Recall Solution

Cause: with no p0, curve_fit defaults every parameter to . Nonlinear least squares is non-convex — the error surface has many valleys — so a poor seed can slide into a wrong valley or fail to converge. Contrast: linear fits like NumPy polyfit need no seed because their error surface is a single bowl (convex), solvable in closed form. Fix: supply a physically sensible seed, e.g. curve_fit(model, x, y, p0=[y.max(), 1.0]). Answer: default seed + non-convex surface → divergence; fix with a good p0.


Recall Feynman recap: the whole ladder in one breath

You learned to spot the right engine (L1), drive each one (L2), see why Newton and least-squares work the way they do (L3), bridge two tools on one problem (L4), and diagnose the quiet failure modes — double roots, missing seeds, deceptive residuals (L5). One skeleton underneath it all: guess, look at the local shape, step toward better, repeat.