Skip to main content

jaxplorer

A compiler explorer TUI for JAX. Put a jitted function on the left, watch its jaxpr, StableHLO and optimized HLO on the right, and see all three change as you type.

┌─ source ─────────┬─ Jaxpr │ StableHLO │ Optimized HLO │ Analysis │ Passes │ LLVM IR ─┐
│ def f(x, w):              │ { lambda ; a:f32[8,16] b:f32[16,4]. let                 │
│     return jnp.tanh(x @ w)│     c:f32[8,4] = dot_general[...] a b                   │
│                           │     d:f32[8,4] = tanh c                                 │
└───────────────────────────┴────────────────────────────────────────────────────────┘
 cpu · jax 0.11.0 · 134 ms · ok

Installation

uv sync                             # in a checkout
uv tool install "jaxplorer[jax]"    # standalone, with its own jax
uv tool install jaxplorer           # standalone, borrowing a project's jax (see below)

Needs Python 3.12+ and textual >= 6.0. jax >= 0.9 is an extra, since the jax worth inspecting is usually a project's own. Any CPU-only jax install is enough; a GPU or TPU backend is only needed to compile for one.

Usage

uv run jaxplorer                       # scratch buffer
uv run jaxplorer mlp                   # open a bundled example (mlp, scan, attention)
uv run jaxplorer my_model.py           # open a snippet and edit it in place
uv run jaxplorer my_model.py --watch    # keep editing in your own editor; jaxplorer reloads on save
uv run jaxplorer mlp --print optimized_hlo   # print one pane and exit, no TUI
key
f1 or ? list every key (the footer only fits a few)
ctrl+r recompile now
ctrl+s save the buffer
ctrl+z, ctrl+y undo, redo (cmd+z / cmd+y also work)
ctrl+f or / find in the active pane; n / N cycle the hits, escape clears
f2 switch backend (skips ones that already failed here)
f3 show or hide the HLO debug tables
f4 diff pass snapshots as graphs instead of as text
f6 collect per-pass HLO and LLVM IR, then recompile
alt+1alt+7 jump to a pane
down from the tab bar into the IR, then arrows scroll it
], [ next, previous section in the Passes pane
y copy the active pane to the clipboard
escape from the IR back to the tab bar
ctrl+q quit

Click an instruction in the Optimized HLO or Passes pane to select the source line that produced it.

Other options: --version, --python PATH (compile under another environment's jax, see below), --platform cpu|gpu|tpu, --x64, --timeout SECONDS, --stages jaxpr,stablehlo,... (the chain stops after the last stage asked for, so leaving out optimized_hlo skips XLA — most of a compile on a large model), --passes to collect per-pass HLO and LLVM IR from the start, --structural-diff to start with f4 on, --print PANE to write one pane to stdout and exit instead of starting the TUI, and --examples to list the bundled snippets.

Against your own project's jax

jaxplorer compiles in a subprocess, and that subprocess can be your project's interpreter:

cd my-project
uv run jaxplorer model.py                     # lookup via uv (VIRTUAL_ENV)
jaxplorer model.py --python .venv/bin/python   # or name the interpreter outright.

The interpreter is chosen in this order: --python, then $VIRTUAL_ENV, then the one running jaxplorer. uv run and a plain activate both export VIRTUAL_ENV, so inside a project the flag is rarely needed.

Anatomy of a snippet

A snippet is an ordinary Python module that defines a callable f and a tuple args of example inputs. Nothing needs to import jaxplorer.

import jax
import jax.numpy as jnp


def f(x, w):
    return jnp.tanh(x @ w).sum()


args = (
    jax.ShapeDtypeStruct((8, 16), jnp.float32),
    jax.ShapeDtypeStruct((16, 4), jnp.float32),
)

args may hold concrete arrays or jax.ShapeDtypeStruct specs. jaxplorer only traces and compiles f, never runs it, so shape specs are enough. Optionally define kwargs, static_argnums, static_argnames or donate_argnums; they are passed to jax.jit. Three examples ship in the wheel — jaxplorer mlp, jaxplorer scan, jaxplorer attention — for an MLP, a lax.scan loop, and causal attention with a static argument. They open as a scratch buffer, so editing one cannot write over your installation.

How it works

Each pane is one step of JAX's public lowering chain:

pane source
Jaxpr jax.jit(f).trace(*args).jaxpr
StableHLO .lower().as_text()
Optimized HLO .compile().as_text(), after XLA's optimization passes
Analysis .cost_analysis() and .memory_analysis()
Passes a snapshot between every XLA pass, diffed to show which pass changed what
LLVM IR the CPU backend's LLVM IR, after LLVM's own passes

Stages are reported independently, so a lowering failure still leaves you a valid jaxpr to read, and a buffer that does not even parse keeps the last IR that did compile on screen.

Compilation happens in a subprocess (python -m jaxplorer.worker) that stays warm between edits: JAX takes seconds to boot, XLA can abort the process outright, and the platform and x64 flags can only be set before JAX is imported.

For the rest of the pipeline (per-pass HLO dumps, LLVM IR, object code, and comparing two XLA builds against each other) see docs/xla-introspection.md.

jaxplorer executes the buffer. It is your own code in your own environment, but a snippet is run at module level on every recompile, so treat it the way you would treat python snippet.py.

Development

Contributions welcome. Here's the general testing and formatting workflow of the repo:

uv run --group test pytest
uvx prek run --all-files
uv run --group typing ty check

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jaxplorer-0.2.0.tar.gz (105.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jaxplorer-0.2.0-py3-none-any.whl (64.8 kB view details)

Uploaded Python 3

File details

Details for the file jaxplorer-0.2.0.tar.gz.

File metadata

  • Download URL: jaxplorer-0.2.0.tar.gz
  • Upload date:
  • Size: 105.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for jaxplorer-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a802167cd51d1c59e1224bbe8fe6faab5709fe884a1695e939457a096da719b8
MD5 cd47a7f8dfec68dc877fb8394acb3cf9
BLAKE2b-256 12c251d645537346b6e7a9eec3a72363763c3811d5d0bb960a3c532efa66dead

See more details on using hashes here.

Provenance

The following attestation bundles were made for jaxplorer-0.2.0.tar.gz:

Publisher: release.yml on nicholasjng/jaxplorer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jaxplorer-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: jaxplorer-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 64.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for jaxplorer-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 52568f4cf108dd66289f579c0154a790eb9e28867eee32e0bc1c290a74933f19
MD5 2541230bafd113c9cf3d4921c5d7050a
BLAKE2b-256 a12aadfd5911d571eabd551db26a9a879944bb0c85ad0df530e598ab36b098b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for jaxplorer-0.2.0-py3-none-any.whl:

Publisher: release.yml on nicholasjng/jaxplorer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page