-
Notifications
You must be signed in to change notification settings - Fork 220
Standardize docs across nx, exla, torchx #1770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
05d66a1
c04e09f
e21b41f
dd75511
d2630d6
7a37a57
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Backend documentation | ||
|
|
||
| EXLA-specific documentation for Nx backend behaviour. | ||
|
|
||
| These guides document divergent behaviour, backend-specific options, and | ||
| limitations for Nx operations. They mirror the structure of the Nx API — see | ||
| the [backend documentation convention](https://hexdocs.pm/nx/backend_documentation-convention.html). | ||
|
|
||
| ## Guides | ||
|
|
||
| * [Nx](backend_documentation-nx.html) — top-level `Nx` blocks, transfers, and `defn` integration | ||
| * [Nx.LinAlg](backend_documentation-nx_lin_alg.html) — linear algebra blocks | ||
|
|
||
| Implementation code lives in `EXLA.Defn`, `EXLA.CustomCall`, and related modules. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # Nx (EXLA) | ||
|
|
||
| EXLA-specific notes for top-level `Nx` operations where behaviour diverges from | ||
| the portable Nx API or from other backends. | ||
|
|
||
| Linear algebra is documented separately in [Nx.LinAlg](backend_documentation-nx_lin_alg.html). | ||
|
|
||
| ## runtime_call/4 | ||
|
|
||
| Runtime Elixir callback from `defn` (`runtime_call` expression). | ||
|
|
||
| ### Platforms | ||
|
|
||
| * **Host / CUDA** — supported via a device-side callback bridged through | ||
| `EXLA.MLIR.Value.runtime_call/3` | ||
| * **ROCm / TPU** — raises with a message to use `:host` or `:cuda` | ||
|
|
||
| ### Outside `defn` | ||
|
|
||
| Executes the callback directly on the input backend without compilation. | ||
|
|
||
| ### Warnings | ||
|
|
||
| Avoid `Nx.backend_transfer/2` on callback tensors inside the function when | ||
| using `Nx.Defn.Evaluator`. Avoid running other Nx computations on the same GPU | ||
| device from within the callback (deadlock risk). | ||
|
|
||
| ## backend_copy/2 | ||
|
|
||
| Copy tensor data to another backend (`EXLA.Backend.backend_copy/3`). | ||
|
|
||
| ### Behaviour | ||
|
|
||
| Copying to `EXLA.Backend` on the same client and device returns the tensor | ||
| unchanged. Cross-device copies use `EXLA.DeviceBuffer.copy_to_device/3`. | ||
| Copying out reads device memory via `EXLA.DeviceBuffer.read/1` and delegates | ||
| to the target backend's `from_binary/3`. | ||
|
|
||
| ### Options | ||
|
|
||
| * `:client`, `:device_id` — select the EXLA client and device | ||
|
|
||
| ## backend_transfer/2 | ||
|
|
||
| Transfer tensor data to another backend (`EXLA.Backend.backend_transfer/3`). | ||
|
|
||
| ### Behaviour | ||
|
|
||
| Same as `backend_copy/2` followed by deallocation of the source device buffer | ||
| when leaving `EXLA.Backend`. | ||
|
|
||
| ### Options | ||
|
|
||
| * `:client`, `:device_id` — select the EXLA client and device | ||
|
|
||
| ## to_pointer/2 | ||
|
|
||
| Export device memory as a pointer (`EXLA.Backend.to_pointer/2`). | ||
|
|
||
| ### Modes | ||
|
|
||
| * `:local` — supported on **host** and **CUDA** clients; returns a local | ||
| address integer | ||
| * `:ipc` — supported on **host** (`shm_open` handle) and **CUDA** (IPC | ||
| handle plus device id) | ||
|
|
||
| ### Options | ||
|
|
||
| * `:permissions` — octal file mode for host IPC shared memory (default | ||
| `0o400`) | ||
|
|
||
| ### Limitations | ||
|
|
||
| Not supported for ROCm, TPU, or non-device buffers. | ||
|
|
||
| ## from_pointer/5 | ||
|
|
||
| Import device memory from a pointer (`EXLA.Backend.from_pointer/5`). | ||
|
|
||
| ### Behaviour | ||
|
|
||
| Creates an `EXLA.DeviceBuffer` from a local address or IPC handle on **host** | ||
| or **CUDA** clients. Pointer `data_size` must match the tensor byte size. | ||
|
|
||
| ### Options | ||
|
|
||
| * `:client`, `:device_id` — destination device | ||
| * `:names` — tensor names for the result template | ||
|
|
||
| ### Limitations | ||
|
|
||
| Not supported for ROCm or TPU. | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,69 @@ | ||||||||||||
| # Nx.LinAlg (EXLA) | ||||||||||||
|
|
||||||||||||
| EXLA-specific notes for `Nx.LinAlg` where behaviour diverges from the portable | ||||||||||||
| Nx API or from other backends. | ||||||||||||
|
|
||||||||||||
| On GPU and TPU clients, the `:precision` option passed to `Nx.Defn.jit/2` | ||||||||||||
| affects accumulator precision for many composed linear algebra graphs compiled | ||||||||||||
| from the default callbacks. For decomposition verification tests, prefer | ||||||||||||
| `precision: :highest`. | ||||||||||||
|
|
||||||||||||
| ## qr/2 | ||||||||||||
|
|
||||||||||||
| QR decomposition (`%Nx.Block.LinAlg.QR{}`). | ||||||||||||
|
|
||||||||||||
| ### Supported platforms | ||||||||||||
|
|
||||||||||||
| * **Host** — native CPU custom call for supported **real** dtypes (`f16`, | ||||||||||||
| `bf16`, `f32`, `f64`, and integer types cast to `f32`); default callback for | ||||||||||||
| complex inputs and unsupported types | ||||||||||||
| * **CUDA / ROCm / TPU** — default callback only (no native QR custom call) | ||||||||||||
|
|
||||||||||||
| ### Options | ||||||||||||
|
|
||||||||||||
| * `:mode` — honoured by whichever implementation is selected (`:reduced` or | ||||||||||||
| `:complete`) | ||||||||||||
| * `:eps` — used by the default callback only; ignored by the native custom call | ||||||||||||
|
|
||||||||||||
| ### Numerical notes | ||||||||||||
|
|
||||||||||||
| Native and default implementations may differ slightly in orthogonality of `q` | ||||||||||||
| and triangular structure of `r` within floating-point tolerance. Reconstruction | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
| `q · r` should match the input within reasonable tolerances for the dtype. | ||||||||||||
|
|
||||||||||||
| ## eigh/2 | ||||||||||||
|
|
||||||||||||
| Hermitian eigendecomposition (`%Nx.Block.LinAlg.Eigh{}`). | ||||||||||||
|
|
||||||||||||
| ### Supported platforms | ||||||||||||
|
|
||||||||||||
| * **Host** — native CPU custom call for supported **real** dtypes (`f32`, | ||||||||||||
| `f64`, and integer types cast to `f32`); default callback for complex inputs and | ||||||||||||
| unsupported types | ||||||||||||
| * **CUDA / ROCm / TPU** — default callback only | ||||||||||||
|
|
||||||||||||
| ### Options | ||||||||||||
|
|
||||||||||||
| * `:max_iter` and `:eps` — honoured by the default callback; ignored by the | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
| native custom call | ||||||||||||
|
|
||||||||||||
| ### Numerical notes | ||||||||||||
|
|
||||||||||||
| Eigenvectors are not unique (sign and degenerate subspaces). Compare results | ||||||||||||
| using reconstruction `v · diag(λ) · vᵀ` rather than direct eigenvector equality | ||||||||||||
| across backends. | ||||||||||||
|
|
||||||||||||
| ## svd/2 | ||||||||||||
|
|
||||||||||||
| Singular value decomposition (`%Nx.Block.LinAlg.SVD{}`). | ||||||||||||
|
|
||||||||||||
| ### Options | ||||||||||||
|
|
||||||||||||
| * `:max_iter` — honoured (default iterative algorithm in Nx) | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
| * `:full_matrices?` — honoured | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
|
|
||||||||||||
| ### Numerical notes | ||||||||||||
|
|
||||||||||||
| SVD is iterative; results may vary across devices and precision settings. | ||||||||||||
| Singular values are the most stable quantity to compare across backends. | ||||||||||||
| Use `precision: :highest` on GPU when verifying decompositions. | ||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -82,6 +82,13 @@ defmodule EXLA do | |||||
| your block tag struct; see `EXLA.CustomCall` for the `call/4` contract, | ||||||
| including returning `:skip` to fall back to the block's default Elixir callback. | ||||||
|
|
||||||
| Backend-specific behaviour, options, and limitations for Nx functions are | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| documented in the [Backend documentation](backend_documentation.html) guides | ||||||
| (for example [Nx](backend_documentation-nx.html) and | ||||||
| [Nx.LinAlg](backend_documentation-nx_lin_alg.html)). See also the | ||||||
| [backend documentation convention](https://hexdocs.pm/nx/backend_documentation-convention.html) | ||||||
| in the Nx package. | ||||||
|
|
||||||
| ## Clients | ||||||
|
|
||||||
| The `EXLA` library uses a client for compiling and executing code. | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,7 +70,8 @@ defmodule EXLA.MixProject do | |
|
|
||
| defp deps do | ||
| [ | ||
| {:nx, path: "../nx"}, | ||
| {:nx, "~> 0.12.0"}, | ||
| # {:nx, path: "../nx"}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a stray change |
||
| {:telemetry, "~> 0.4.0 or ~> 1.0"}, | ||
| {:xla, "~> 0.10.0", runtime: false}, | ||
| {:fine, "~> 0.1", runtime: false}, | ||
|
|
@@ -87,9 +88,21 @@ defmodule EXLA.MixProject do | |
| source_url_pattern: "#{@source_url}/blob/v#{@version}/exla/%{path}#L%{line}", | ||
| extras: [ | ||
| "guides/rotating-image.livemd", | ||
| "CHANGELOG.md" | ||
| "CHANGELOG.md", | ||
| "guides/backend_documentation/index.md": [ | ||
| filename: "backend_documentation" | ||
| ], | ||
| "guides/backend_documentation/nx.md": [ | ||
| filename: "backend_documentation-nx" | ||
| ], | ||
| "guides/backend_documentation/nx_lin_alg.md": [ | ||
| filename: "backend_documentation-nx_lin_alg" | ||
| ] | ||
| ], | ||
| skip_undefined_reference_warnings_on: ["CHANGELOG.md"], | ||
| groups_for_extras: [ | ||
| "Backend documentation": ~r"^guides/backend_documentation/" | ||
| ], | ||
| groups_for_modules: [ | ||
| # EXLA, | ||
| # EXLA.Backend, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| defmodule EXLA.BackendDocumentationTest do | ||
| use ExUnit.Case, async: true | ||
|
|
||
| doctest_file("guides/backend_documentation/index.md") | ||
| doctest_file("guides/backend_documentation/nx.md") | ||
| doctest_file("guides/backend_documentation/nx_lin_alg.md") | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Backend documentation convention | ||
|
|
||
| Nx exposes a stable public API (`Nx`, `Nx.LinAlg`, and so on) while individual | ||
| backends implement the underlying callbacks and optional `Nx.block/4` lowerings. | ||
| Users who need to know how a specific backend differs from the portable API should | ||
| not have to read backend source code. | ||
|
|
||
| ## Documentation guides | ||
|
|
||
| Each backend may publish **backend documentation guides** that mirror the Nx | ||
| module structure. The naming convention is: | ||
|
|
||
| guides/backend_documentation/nx.md # mirrors top-level `Nx` | ||
| guides/backend_documentation/nx_lin_alg.md # mirrors `Nx.LinAlg` | ||
|
|
||
| For example, EXLA documents operations in its [Backend documentation](https://hexdocs.pm/exla/backend_documentation.html) | ||
| guides, and Torchx in its [Backend documentation](https://hexdocs.pm/torchx/backend_documentation.html) | ||
| guides. | ||
|
|
||
| ## What to document | ||
|
|
||
| Document an operation only when a backend has **divergent behaviour**, **backend-specific | ||
| options**, or **trade-offs and limitations** worth calling out relative to the | ||
| portable Nx API or to other backends. Examples: | ||
|
|
||
| * an option that a backend honours, ignores, or overrides | ||
| * platform or device support gaps (for example an operation unavailable on MPS) | ||
| * numerical or performance trade-offs that affect how you use the API | ||
| * warnings about deadlock, unsupported pointer modes, and similar constraints | ||
|
|
||
| Do not document routine lowerings (for example which StableHLO op or LibTorch | ||
| kernel is used) when behaviour matches the reference Nx implementation. If you | ||
| are implementing or overriding custom blocks, the backend source remains the best | ||
| source of truth. | ||
|
|
||
| Cross-link from the Nx function doc when a backend guide has relevant notes for | ||
| that operation. | ||
|
|
||
| ## Relation to `Nx.Backend` | ||
|
|
||
| Most tensor operations go through `Nx.Backend` callbacks. Operations that need | ||
| a portable default with optional native acceleration use `Nx.block/4` and the | ||
| `Nx.Block.*` tags — see `Nx.Backend.block/4`. Backend documentation guides | ||
| should cover divergent behaviour for both callback implementations and block | ||
| lowerings. | ||
|
|
||
| ## Example | ||
|
|
||
| ```elixir | ||
| iex> Nx.take(Nx.tensor([10, 20, 30]), Nx.tensor([0, 2])) | ||
| #Nx.Tensor< | ||
| s32[2] | ||
| [10, 30] | ||
| > | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is exactly the kind of documentation that this PR let's shine through!