#set document(title: "1.3 Harvesting and Bifurcation", author: "XYZ Homework") #set page(width: 8.5in, height: auto, margin: 1in) #import "@preview/cetz:0.5.2" #set text(font: ("STIX Two Text", "Libertinus Serif", "New Computer Modern"), size: 10.5pt, lang: "en") #show math.equation: set text(font: ("STIX Two Math", "New Computer Modern Math")) #set par(justify: true, leading: 0.62em, spacing: 0.9em) #set enum(spacing: 1.1em) // room between list items so tall inline fractions don't collide #set list(spacing: 1.1em) #set table(stroke: 0.5pt + rgb("#c7ccd3")) #let BLUE = rgb("#183B6F") // brand navy — section bars + example/solution labels (white on navy 11.09:1) #let ORANGE = rgb("#A94509") // brand primary-700 — AA-safe deep orange for TEXT (5.93:1 on white; raw brand #F37021 is 2.94:1 and must never carry text) #let RED = rgb("#DC2626") // brand error-600 #let GREEN = rgb("#059669") // brand success-600 (decoration only; small green text uses green-text #007942) #show heading.where(level: 1): it => block(width: 100%, above: 0pt, below: 16pt, fill: gradient.linear(BLUE, rgb("#2C5AA0")), inset: (x: 14pt, y: 12pt), radius: 3pt, text(fill: white, weight: "bold", size: 19pt, it.body)) #show heading.where(level: 2): it => block(width: 100%, above: 18pt, below: 10pt, fill: BLUE, inset: (x: 10pt, y: 6pt), radius: 2pt, text(fill: white, weight: "bold", size: 12pt, it.body)) #show heading.where(level: 3): it => text(fill: ORANGE, weight: "bold", size: 12.5pt, it.body) #show heading.where(level: 4): it => text(fill: BLUE, weight: "bold", size: 10.5pt, it.body) #let examplebox(label, title, body) = block(width: 100%, breakable: true, fill: rgb("#EFF1F5"), stroke: 0.5pt + rgb("#CFDDF0"), radius: 4pt, inset: 10pt, above: 12pt, below: 12pt)[ #block(below: 6pt)[#box(fill: BLUE, inset: (x: 6pt, y: 2pt), radius: 2pt, text(fill: white, weight: "bold", size: 8.5pt, label)) #h(0.4em) #strong[#title]] #body] // rail = decorative left rule (raw brand token); labelcolor = AA-safe label text shade #let notebox(label, rail, labelcolor, tint, body) = block(width: 100%, breakable: true, fill: tint, stroke: (left: 3pt + rail), inset: (left: 10pt, rest: 8pt), radius: (right: 4pt), above: 11pt, below: 11pt)[ #text(fill: labelcolor, weight: "bold", size: 7.5pt, tracking: 0.5pt)[#upper(label)] #linebreak() #body] #let solutionbox(body) = block(above: 4pt, below: 8pt)[ #text(fill: BLUE, weight: "bold", size: 8.5pt)[Solution] #linebreak() #body] #let figph(msg) = block(width: 100%, height: 60pt, fill: rgb("#f6f7f9"), stroke: (paint: rgb("#c7ccd3"), dash: "dashed"), radius: 4pt, inset: 10pt)[ #align(center + horizon, text(fill: rgb("#889"), style: "italic", size: 9pt, msg))] // Standardize inlined figure sizes: measure the natural CeTZ canvas, then scale to a // consistent envelope (aspect-aware; see build_typst.py FIG_* constants). Unlike the // print preamble, dimensions are FLOORED: in an editor a user can trim a figure to a // degenerate 1-D shape (a bare line), and w/h or tw/w would then divide by zero. #let _STD_W = 3.5 #let _WIDE_W = 5.6 #let _MAX_H = 3.4 #let _ASPECT_WIDE = 2.2 #let _UPSCALE_MAX = 1.15 #let stdfig(body) = context { let m = measure(body) let w = calc.max(m.width / 1in, 0.01) let h = calc.max(m.height / 1in, 0.01) let tw = if w / h > _ASPECT_WIDE { _WIDE_W } else { _STD_W } let s = calc.min(tw / w, _MAX_H / h, _UPSCALE_MAX) align(center, box(scale(x: s * 100%, y: s * 100%, reflow: true, body))) } #show figure: set block(breakable: false) #set figure(gap: 8pt) #show figure.caption: set text(size: 8.5pt, fill: rgb("#555")) == 1.3#h(0.6em)Harvesting and Bifurcation #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[Objectives] - Modify a population model to include constant-rate harvesting and find the resulting equilibria. - Track how equilibria move, collide, and vanish as a parameter is varied. - Identify the critical harvest rate and explain the saddle-node bifurcation in plain language. ] Take last section's pond and start fishing it. If boats remove a fixed quantity per unit time — a quota, not a percentage — the model gains a constant subtraction: #math.equation(block: true, alt: "the fraction d P over d t equals 2 P open parenthesis 1 minus the fraction P over 8 close parenthesis minus a ,")[$frac(d P, d t) = 2 P ( 1 − frac(P, 8) ) − a ,$] where #math.equation(block: false, alt: "a greater than or equal to 0")[$a ≥ 0$] is the harvest rate. The question a fishery manager actually asks is not "what is #math.equation(block: false, alt: "P open parenthesis t close parenthesis")[$P ( t )$]?" but "how large can #math.equation(block: false, alt: "a")[$a$] be before the pond collapses?" That question is about equilibria, and equilibria can be found without solving anything: set the right-hand side to zero. Expanding, #math.equation(block: false, alt: "2 P minus P squared / 4 minus a equals 0")[$2 P − P^(2) / 4 − a = 0$], or #math.equation(block: false, alt: "P squared minus 8 P plus 4 a equals 0")[$P^(2) − 8 P + 4 a = 0$], whose roots are #math.equation(block: true, alt: "P sub ± equals 4 ± the square root of 16 minus 4 a .")[$P_(±) = 4 ± sqrt(16 − 4 a) .$] Everything interesting is in that square root. For #math.equation(block: false, alt: "a less than 4")[$a < 4$] there are two equilibria, one above the midpoint #math.equation(block: false, alt: "4")[$4$] and one below. At #math.equation(block: false, alt: "a equals 4")[$a = 4$] the root vanishes and the two merge into a single equilibrium at #math.equation(block: false, alt: "P equals 4")[$P = 4$]. For #math.equation(block: false, alt: "a greater than 4")[$a > 4$] there is no real root at all: no equilibrium exists, #math.equation(block: false, alt: "d P / d t")[$d P / d t$] is negative for every population, and the fishery drives itself to zero no matter how healthy it started. The figure below shows the two equilibrium levels as horizontal lines whose heights you control with the harvest slider. {"camera":{"fov":50,"position":\[5,4,11\],"projection":"perspective","target":\[5,4,0\]},"grid":{"axisColors":{"x":"\#ef4444","y":"\#22c55e","z":"\#3b82f6"},"divisions":10,"gridColor":"\#e5e7eb","showAxes":true,"showLabels":true,"size":10,"visible":true},"id":"2ef39e0b-84de-4322-8ed4-45e7aa19ef48","metadata":{"alt\_text":"Two horizontal lines in the plane: a solid green line (the stable equilibrium population) and a dashed red line below it (the unstable threshold). Dragging the harvest slider a from 0 toward 4 pulls the green line down from 8 and the red line up from 0 until the two meet at height 4 exactly when a reaches 4, where both equilibria disappear together.","created\_at":"2026-08-01T00:00:00.000Z","description":"Equilibria P = 4 +/- sqrt(16 - 4a) of the harvested logistic equation dP/dt = 2P(1 - P/8) - a, drawn as horizontal lines whose heights track the harvest slider a in \[0, 4\]; the saddle-node collision happens at a = 4.","tags":\["diffeq","first-order","bifurcation","harvesting","slider"\],"updated\_at":"2026-08-01T00:00:00.000Z"},"objects":\[{"id":"9fef608d-57f0-4746-ba74-5bccfff201c0","kind":"parameter","label":{"text":"a = constant harvest rate","visible":true},"max":4,"min":0,"name":"a","step":0.05,"value":1,"visible":true},{"id":"751de972-9645-42c5-bd74-e3fa7b74626b","kind":"parametric-curve","label":{"text":"stable equilibrium P+","visible":true},"samples":100,"style":{"color":"\#16a34a","dash":\[\],"lineWidth":4},"tDomain":\[0,10\],"visible":true,"xExpr":"t","yExpr":"4 + sqrt(max(16 - 4\*a, 0))","zExpr":"0"},{"id":"a0cab50e-6d61-46b1-8795-a2cd99cfb47c","kind":"parametric-curve","label":{"text":"unstable equilibrium P-","visible":true},"samples":100,"style":{"color":"\#dc2626","dash":\[6,4\],"lineWidth":3},"tDomain":\[0,10\],"visible":true,"xExpr":"t","yExpr":"4 - sqrt(max(16 - 4\*a, 0))","zExpr":"0"}\],"title":"Harvested logistic equilibria collide at a = 4","version":1} Equilibria of the harvested logistic model. The solid green line is #math.equation(block: false, alt: "P sub plus equals 4 plus the square root of 16 minus 4 a")[$P_(+) = 4 + sqrt(16 − 4 a)$] and the dashed red line is #math.equation(block: false, alt: "P sub minus equals 4 minus the square root of 16 minus 4 a")[$P_(−) = 4 − sqrt(16 − 4 a)$]. Dragging the harvest rate #math.equation(block: false, alt: "a")[$a$] toward #math.equation(block: false, alt: "4")[$4$] squeezes the two together until they collide and disappear. === Explore + #strong[Observe.] Start with #math.equation(block: false, alt: "a equals 0")[$a = 0$] (no fishing). Where do the two lines sit? Confirm they are the equilibria #math.equation(block: false, alt: "0")[$0$] and #math.equation(block: false, alt: "8")[$8$] you already know from the unharvested logistic equation. + #strong[Predict.] As #math.equation(block: false, alt: "a")[$a$] increases, one line must move up and the other down. Which is which, and why? Write down your reasoning from the formula before dragging. + #strong[Verify.] Drag #math.equation(block: false, alt: "a")[$a$] slowly from #math.equation(block: false, alt: "0")[$0$] to #math.equation(block: false, alt: "3")[$3$]. Note the gap between the lines at #math.equation(block: false, alt: "a equals 1")[$a = 1$], at #math.equation(block: false, alt: "a equals 2")[$a = 2$], and at #math.equation(block: false, alt: "a equals 3")[$a = 3$]. Is the gap shrinking at a steady rate, or is it collapsing faster near the end? + #strong[Observe.] Push #math.equation(block: false, alt: "a")[$a$] to #math.equation(block: false, alt: "3.9")[$3.9$], then to #math.equation(block: false, alt: "3.99")[$3.99$]. Both lines are now nearly at height #math.equation(block: false, alt: "4")[$4$]. The square root is what makes the last stretch so abrupt — the gap is #math.equation(block: false, alt: "2 the square root of 16 minus 4 a")[$2 sqrt(16 − 4 a)$], so what happens to the #emph[rate] of collapse as #math.equation(block: false, alt: "a → 4")[$a → 4$]? + #strong[Predict, then reason.] The green line is stable and the red one unstable. If the pond is sitting comfortably at #math.equation(block: false, alt: "P sub plus")[$P_(+)$] with #math.equation(block: false, alt: "a equals 3.9")[$a = 3.9$] and a storm knocks the population down by #math.equation(block: false, alt: "1")[$1$], does it recover? Now suppose the harvest is raised to #math.equation(block: false, alt: "a equals 4.1")[$a = 4.1$] instead: what happens to a pond sitting at #math.equation(block: false, alt: "P equals 4")[$P = 4$]? === Connect A #emph[bifurcation] is a qualitative change in the set of equilibria caused by a smooth change in a parameter. Here, nudging #math.equation(block: false, alt: "a")[$a$] through the critical value #math.equation(block: false, alt: "4")[$4$] does not merely move the equilibria — it destroys them. Two equilibria of opposite stability approach each other, merge, and annihilate; this pattern is common enough to have a name, the #emph[saddle-node] (or fold) bifurcation, and it is the mathematical signature of a system that fails suddenly rather than gradually. The management lesson is sharper than the equations look. Just below the critical rate, the pond still has a stable equilibrium, so the fishery appears to be working — but the unstable equilibrium #math.equation(block: false, alt: "P sub minus")[$P_(−)$] has crept up close underneath it, which means the #emph[margin for error] has nearly vanished. A bad season that pushes the population below #math.equation(block: false, alt: "P sub minus")[$P_(−)$] sends it to extinction even though the harvest rate never changed. Systems near a saddle-node bifurcation look healthy right up until they do not, and the shrinking gap you watched in step 3 is exactly the shrinking margin. #examplebox("Example 1")[The last safe quota][ With #math.equation(block: false, alt: "a equals 3")[$a = 3$], find both equilibria and the size of the shock the population can absorb from the stable one. Then find the largest quota for which any equilibrium at all exists. At #math.equation(block: false, alt: "a equals 3")[$a = 3$], #math.equation(block: false, alt: "the square root of 16 minus 12 equals 2")[$sqrt(16 − 12) = 2$], so #math.equation(block: false, alt: "P sub plus equals 6")[$P_(+) = 6$] and #math.equation(block: false, alt: "P sub minus equals 2")[$P_(−) = 2$]. A population resting at #math.equation(block: false, alt: "6")[$6$] survives any shock smaller than #math.equation(block: false, alt: "6 minus 2 equals 4")[$6 − 2 = 4$]; a larger crash drops it below #math.equation(block: false, alt: "P sub minus")[$P_(−)$], where #math.equation(block: false, alt: "d P / d t less than 0")[$d P / d t < 0$] forever, and the pond empties. Equilibria exist exactly when #math.equation(block: false, alt: "16 minus 4 a greater than or equal to 0")[$16 − 4 a ≥ 0$], that is #math.equation(block: false, alt: "a less than or equal to 4")[$a ≤ 4$], so the largest sustainable quota is #math.equation(block: false, alt: "a equals 4")[$a = 4$] — at which the safety margin has shrunk to zero, since #math.equation(block: false, alt: "P sub plus equals P sub minus equals 4")[$P_(+) = P_(−) = 4$]. A manager who targets the theoretical maximum has built a fishery with no tolerance for a bad year. ] #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[Coming soon: Euler's method] Everything in this section was equilibrium reasoning, which needs no solution formula — fortunate, because the harvested logistic equation's solutions are unpleasant to write down and most equations have none at all. The general escape is numerical: #emph[Euler's method] steps forward in small increments, using the equation's own slope at each point to guess the next, and refining the step size until the answer stops changing. The figure for it — a staircase of Euler steps overlaid on a true solution curve, with a slider on the step size to watch the error shrink — needs the trajectory object being added to the XYZ 3D engine, which integrates a differential equation on the fly rather than plotting a formula. It arrives with the same release as the slope field. ]