JAX Core Concepts
Master JAX's fundamental building blocks: DeviceArrays, JIT compilation for speed, automatic differentiation with grad, and automatic vectorization with vmap.
DeviceArray (jax.Array)
JAX arrays work like NumPy arrays but live on accelerator devices (GPU/TPU) and are immutable:
import jax.numpy as jnp # Create arrays (just like NumPy!) x = jnp.array([1.0, 2.0, 3.0]) zeros = jnp.zeros((3, 4)) rand = jax.random.normal(jax.random.PRNGKey(0), (3, 3)) # NumPy-like operations y = jnp.dot(x, x) z = jnp.sin(x) + jnp.cos(x) # Immutable! Use .at[] for "updates" # x[0] = 5 # ERROR! JAX arrays are immutable x_new = x.at[0].set(5.0) # Creates a new array
JIT Compilation
jax.jit compiles a function into optimized XLA code. The first call traces and compiles; subsequent calls run the compiled version:
import jax # Method 1: Decorator @jax.jit def fast_computation(x, y): return jnp.dot(x, y) + jnp.sum(x) # Method 2: Wrap function def slow_fn(x): return jnp.sum(jnp.sin(x) ** 2) fast_fn = jax.jit(slow_fn) # First call: compile + run (slower) result = fast_fn(jnp.ones(1000000)) # Subsequent calls: just run (much faster!) result = fast_fn(jnp.ones(1000000))
jax.lax.cond for conditional logic and jax.lax.fori_loop for loops.
Automatic Differentiation (grad)
jax.grad computes gradients of scalar-valued functions. It is the foundation of all training in JAX:
import jax # Gradient of a scalar function def loss_fn(w, x, y): predictions = jnp.dot(x, w) return jnp.mean((predictions - y) ** 2) # Compute gradient with respect to first argument (w) grad_fn = jax.grad(loss_fn) grads = grad_fn(w, x, y) # Value and gradient together (more efficient) loss, grads = jax.value_and_grad(loss_fn)(w, x, y) # Higher-order derivatives def f(x): return x ** 3 df = jax.grad(f) # 3x^2 d2f = jax.grad(df) # 6x d3f = jax.grad(d2f) # 6 print(d2f(2.0)) # 12.0 (6 * 2)
Vectorization (vmap)
jax.vmap automatically vectorizes a function to operate over batches. Write code for a single example, then vmap it to handle batches efficiently:
import jax # Function for a single data point def predict_single(params, x): return jnp.dot(params['w'], x) + params['b'] # Automatically vectorize over a batch of inputs predict_batch = jax.vmap(predict_single, in_axes=(None, 0)) # None = don't vectorize params, 0 = vectorize x along first axis # Works on entire batches! params = {'w': jnp.ones(10), 'b': 0.0} batch_x = jnp.ones((32, 10)) # 32 samples predictions = predict_batch(params, batch_x) # (32,) # Compose with other transforms fast_batch_predict = jax.jit(jax.vmap(predict_single, in_axes=(None, 0))) # Per-sample gradients (impossible in PyTorch without tricks!) per_sample_grads = jax.vmap(jax.grad(loss_fn), in_axes=(None, 0, 0))
jax.vmap(jax.grad(fn)). This composability is what makes JAX uniquely powerful.
Next Up: Neural Networks
Now that you understand JAX's core transforms, let's build neural networks with Flax and Haiku and train them with Optax.
Next: Neural Networks →Ready to Go Deeper?
Live instructor-led courses from our partners. Affiliate disclosure.
AI & ML Courses - 30% Off
Live instructor-led AI, machine learning, data science, and cloud courses for working professionals. Use code Limited30 at checkout.
EdurekaDataCamp - AI & Data Science
Hands-on Python, machine learning, and AI courses with interactive exercises and real projects.
DataCampedX - Top AI Courses
University-level AI courses from MIT, Harvard, Stanford. Earn certificates that employers recognize.
edX