[career] Convert to JAX using nested vmap (supersedes #617)#985
Merged
Conversation
Rewrites the career choice lecture to use JAX on the GPU, replacing the CareerWorkerProblem class, operator_factory, and the nested prange loops. The three options in the Bellman equation are written as a scalar function _B of one state, returning them in the order they appear in the text. Two applications of jax.vmap then evaluate it at every state, and T and get_greedy become the max and the argmax of that same array -- which removes the duplicated grid-search code the two functions previously carried, and makes the 1/2/3 action encoding explicit rather than buried in a branch. Other changes: - Model primitives move to a NamedTuple with a factory function. - solve_model uses a bounded jax.lax.while_loop and returns the iteration count and final error so callers can check convergence. - Deletes gen_probs. It computed the beta-binomial pmf by hand, and is bit-for-bit identical to BetaBinomial(n, a, b).pdf() from quantecon, which the lecture already imports and uses for F_probs and G_probs. This also drops the scipy.special import. - Exercise 1 simulates paths with jax.lax.scan. - Exercise 2 writes one first-passage simulation as a bounded jax.lax.while_loop and runs 25,000 of them at once under jax.vmap. This is the part of the lecture that genuinely benefits from a GPU. - Adds the shared GPU admonition. Validated against the Numba implementation on main: value functions agree to 1.8e-04 (relative 9.0e-07), and the greedy policy is identical at all 2500 grid cells. The median first passage time is 7 at the default parameters and 14 at β=0.99, matching the values stated in the text. All fourteen code cells execute on an RTX 4080 and the five figures reproduce the reference images. Supersedes #617. Co-Authored-By: Humphrey Yang <u6474961@anu.edu.au> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📖 Netlify Preview Ready!Preview URL: https://pr-985--sunny-cactus-210e3e.netlify.app Commit: 📚 Changed LecturesBuild Info
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Converts
career.mdto JAX. This supersedes #617 by @HumphreyYang and builds on it — theNamedTuplerefactor, the vectorized Bellman operator and thevmap-over-while_loopapproach to the first passage times all come from that PR. Humphrey is credited as co-author on the commit.Same treatment as #984 for
jv.md, and same reason for a separate branch: reviewing #617 turned into a wider discussion about when a lecture should use JAX, and starting frommainwas simpler than stacking further requests onto that branch.Why JAX belongs here
Unlike some conversions, this one has a genuinely parallel workload today rather than a promise about larger grids: {ref}
career_ex2runs 25,000 independent first-passage simulations, which is exactly whatvmapplus a GPU is for.Approach
The Bellman equation is$Tv(\theta,\epsilon) = \max{I, II, III}$ . Those three options become a scalar function of one state, written in the order the text presents them:
Two
vmaps evaluate it at every state, and thenBecause both reduce over the same array, the ~20 lines of grid-search code that
Tandget_greedyused to duplicate disappear, and the 1/2/3 action encoding becomes explicit rather than buried in an if/elif chain.gen_probsis deletedThe helper that computed the beta-binomial pmf by hand is bit-for-bit identical to
BetaBinomial(n, a, b).pdf()— which the lecture already imports and already uses forF_probsandG_probs. Verified across(a,b)= (0.5,0.5), (1,1), (2,5), (100,100).This also removes the
from scipy.special import binom, betaimport.Other changes
solve_modeluses a boundedjax.lax.while_loopand returns the iteration count and final error, so non-convergence is visible rather than silent.jax.lax.scan, which also disposes of the brokenquantecon.jr.draw()hint rather than patching it.while_loopgets the iteration bound it was missing.{include} _admonition/gpu.md; no!pip install jaxand no CPU pin. Both CI and publish run ong4dn.2xlargewithjax[cuda13], and on a GPU box the passage-time simulation is ~87× faster than it is pinned to the CPU.Validation
Checked against the Numba implementation on
main:max abs(v_numba - v_new)1.8e-04(relative9.0e-07)All fourteen code cells execute on a GPU (RTX 4080). I checked the five figures against the reference images: the pmf plot, the value function surface, the policy contour with its new life / new job / stay put regions, the Exercise 1 sample paths (one worker settling, one holding a career while churning jobs), and the Exercise 3 contour with its enlarged stay-put region. Worth confirming against the preview build once CI runs.
Related
jv.mdvmappattern🤖 Generated with Claude Code