Functional programming patterns in JAX -- when and how to use grad, jit, vmap, and pmap. Use when you need help with skill jax patterns.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill skill-jax-patterns --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Skill Jax Patterns?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-skill-jax-patterns)More formats (shields.io, HTML) on the badges page.
---
name: skill-jax-patterns
description: Functional programming patterns in JAX -- when and how to use grad, jit, vmap, and pmap. Use when you need help with skill jax patterns.
license: CC-BY-NC-SA-4.0
phase: 3
lesson: 12
metadata:
version: 1.0.0
tags: [jax, functional-programming, autodiff, compilation, vectorization]
---
# JAX Functional Patterns
JAX transforms pure functions. Every pattern below follows one rule: write a function that takes inputs and returns outputs, with no side effects. Then transform it.
## The Four Transforms
### grad -- Differentiate a function
```python
grads = jax.grad(loss_fn)(params, x, y)
loss, grads = jax.value_and_grad(loss_fn)(params, x, y)
```
Use when: you need gradients for optimization.
Constraint: the function must return a scalar. For non-scalar outputs, use `jax.jacobian`.
### jit -- Compile a function
```python
fast_fn = jax.jit(f)
```
Use when: the function will be called more than once with same-shaped inputs.
Constraint: no Python control flow that depends on traced values. Use `jax.lax.cond` for conditionals, `jax.lax.scan` for loops.
### vmap -- Vectorize a function
```python
batch_fn = jax.vmap(f, in_axes=(None, 0))
```
Use when: you wrote a function for one example and need it to work on batches.
`in_axes` specifies which argument axis to batch over. `None` means do not batch (broadcast).
### pmap -- Parallelize across devices
```python
parallel_fn = jax.pmap(f, axis_name='devices')
```
Use when: you have multiple GPUs/TPUs and want data parallelism.
Inside the function, `jax.lax.pmean(x, 'devices')` averages across devices.
## Composition Rules
Transforms compose. The order matters:
```python
per_example_grads = jax.jit(jax.vmap(jax.grad(loss_fn), in_axes=(None, 0, 0)))
```
Reading right to left: take gradient of loss_fn, vectorize over examples, compile the result.
Valid compositions:
- `jit(grad(f))` -- compiled gradient computation
- `jit(vmap(f))` -- compiled batched computation
- `vmap(grad(f))` -- per-example gradients
- `pmap(jit(f))` -- parallel compiled computation
- `grad(jit(f))` -- gradient of compiled function (same as jit(grad(f)))
## Parameter Management Pattern
JAX parameters are pytrees (nested dicts of arrays):
```python
params = {
'layer1': {'w': jnp.zeros((784, 256)), 'b': jnp.zeros(256)},
'layer2': {'w': jnp.zeros((256, 10)), 'b': jnp.zeros(10)},
}
```
Update all parameters at once:
```python
params = jax.tree.map(lambda p, g: p - lr * g, params, grads)
```
Count parameters:
```python
n_params = sum(p.size for p in jax.tree.leaves(params))
```
## PRNG Key Management
JAX requires explicit random keys:
```python
key = jax.random.PRNGKey(0)
key, subkey = jax.random.split(key)
noise = jax.random.normal(subkey, shape)
```
For multiple random operations, split once:
```python
keys = jax.random.split(key, n)
```
Never reuse a key. Always split before using.
## Common Mistakes
1. **Mutating arrays inside jit**: JAX arrays are immutable. Use `x.at[i].set(v)` instead of `x[i] = v`.
2. **Using Python print inside jit**: `print` runs during tracing, not execution. Use `jax.debug.print("{}", x)`.
3. **Python if/for inside jit on traced values**: Use `jax.lax.cond`, `jax.lax.switch`, `jax.lax.scan`, `jax.lax.fori_loop`.
4. **Forgetting `.block_until_ready()`**: JAX uses async dispatch. For benchmarking, call `.block_until_ready()` to wait for actual completion.
5. **Reusing PRNG keys**: Two operations with the same key produce the same "random" values. Always split.
6. **Global state in jitted functions**: Global variables are captured at trace time. Changes after tracing are invisible. Pass everything as arguments.
## Decision Checklist
1. Is the function called more than once? Add `@jax.jit`.
2. Does it need gradients? Wrap with `jax.grad` or `jax.value_and_grad`.
3. Does it process one example but you have a batch? Wrap with `jax.vmap`.
4. Do you have multiple devices? Wrap with `jax.pmap`.
5. Does it use randomness? Thread PRNG keys through explicitly.
6. Does it have Python control flow on array values? Replace with `jax.lax` primitives.
## When to Use JAX
Use JAX when:
- You need per-example gradients (differential privacy, Fisher information)
- You are training on TPUs (JAX is the native framework)
- You need higher-order derivatives (Hessians, Jacobians)
- You want to compile the entire training step to a single kernel
- Your team is at Google DeepMind or Anthropic
Use PyTorch when:
- You want the largest ecosystem (HuggingFace, torchvision, Lightning)
- You prioritize debugging ease over raw speed
- You are deploying to NVIDIA GPUs with TorchServe/Triton
- You are hiring (more PyTorch developers exist)
- You want to iterate fast on new architectures
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!