jnp is NumPy, but immutablex.at[i].set(v) instead of x[i] = v
jit / grad / vmap composejit(vmap(grad(f))) = compiled, batched, per-example gradients
grad needs a scalar outputvalue_and_grad(loss) is the training-step workhorse
RNG is explicitkey = jax.random.key(0); split before every draw; never reuse
branch with lax, not iflax.cond / jnp.where / lax.scan survive tracing
params are a pytreejax.tree.map applies updates leaf-wise across the whole model
jit recompiles per shapefix shapes; mark shape args static_argnums
side effects vanish under jituse jax.debug.print; return values, don't mutate
sharding via jax.shardingMesh + NamedSharding + plain jit auto-parallelizes (Shardy default)
layers=Flax, optimizers=OptaxJAX is the engine; these are the framework on top