Skip to content

Commit 0dce8e8

Browse files
authored
Merge pull request #15 from ausimian/claude/update-readme-latest-YYbSL
2 parents 7ea032c + 4638bcd commit 0dce8e8

1 file changed

Lines changed: 87 additions & 9 deletions

File tree

README.md

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
Elixir bindings and Nx backend for Apple's [MLX](https://github.com/ml-explore/mlx).
44

5-
**Status: M0 — scaffold.** See [`PLAN.md`](PLAN.md) for the full roadmap.
5+
**Status: M7 — Bumblebee conformance breadth.** Backend (M2), Defn
6+
compiler (M5), and four Bumblebee models (DistilBERT, Qwen3, ViT,
7+
Whisper) run end-to-end. M8 (native `conv`) and M9 (1.0 release) are
8+
next. See [`PLAN.md`](PLAN.md) for the full roadmap and
9+
[`RELEASE.md`](RELEASE.md) for unreleased-version notes.
610

711
## Why
812

@@ -11,24 +15,98 @@ acceleration, via a layered architecture that keeps each layer
1115
independently testable and avoids the [nif_call-deadlock
1216
class](https://github.com/elixir-nx/emlx/issues/88) that grounded EMLX.
1317

18+
## Architecture
19+
20+
```
21+
Emily.Compiler (Nx.Defn.Compiler) — validates opts, pins the result backend
22+
Emily.Backend (Nx.Backend) — op-by-op translation to Native
23+
Emily.Native (thin NIF shim) — one function per MLX op, no policy
24+
MLX C++ (vendored binary) — cocoa-xu/mlx-build prebuilts, pinned
25+
```
26+
27+
One-directional dispatch: Elixir → C++ → MLX. C++ never calls back into
28+
BEAM, so the EMLX #88 deadlock class is structurally impossible.
29+
1430
## Requirements
1531

1632
- macOS (Apple Silicon recommended; x86_64 supported)
1733
- Elixir 1.18+ / OTP 27+ (development pinned to 1.19.5 / OTP 28 via `.tool-versions`)
1834

19-
## Usage (M0)
35+
MLX 0.25.1 is fetched as a prebuilt from
36+
[cocoa-xu/mlx-build](https://github.com/cocoa-xu/mlx-build) during
37+
`mix compile`; no separate install step.
38+
39+
## Usage
40+
41+
Install Emily as the global Nx backend and use Nx normally:
42+
43+
```elixir
44+
Nx.global_default_backend({Emily.Backend, device: :gpu})
45+
46+
Nx.tensor([[1.0, 2.0], [3.0, 4.0]])
47+
|> Nx.dot(Nx.tensor([[5.0], [6.0]]))
48+
|> Nx.to_flat_list()
49+
# => [17.0, 39.0]
50+
```
2051

21-
Only a tiny tensor round-trip is wired up today:
52+
Use `Emily.Compiler` for `defn` / `Nx.Serving`:
2253

2354
```elixir
24-
bin = <<1.0::float-32-native, 2.0::float-32-native, 3.0::float-32-native>>
25-
t = Emily.from_binary(bin, [3], {:f, 32})
26-
Emily.to_binary(t) == bin
27-
Emily.shape(t) == [3]
28-
Emily.dtype(t) == {:f, 32}
55+
Nx.Defn.global_default_options(compiler: Emily.Compiler)
56+
```
57+
58+
Bumblebee inference works with no further configuration once the
59+
backend is installed — see the conformance suites under
60+
`test/emily/conformance/` for worked DistilBERT, Qwen3, ViT, and
61+
Whisper pipelines.
62+
63+
The low-level tensor API (`Emily.from_binary/3`, `to_binary/1`,
64+
`shape/1`, `dtype/1`, `eval/1`) remains available for diagnostics and
65+
direct MLX round-trips, but most users should go through Nx.
66+
67+
## Milestones shipped
68+
69+
- **M0** — NIF scaffold, MLX prebuilt fetch, tensor round-trip.
70+
- **M1**`Emily.Native` op inventory (creation, unary, binary,
71+
reductions, shape, indexing, sort, linalg, FFT, random, memory).
72+
- **M2**`Emily.Backend` (`Nx.Backend`). StreamData property oracle
73+
vs. `Nx.BinaryBackend`; soak + concurrency harnesses.
74+
- **M3** — DistilBERT end-to-end on Bumblebee; native batched `dot`,
75+
type promotion, `bitcast`.
76+
- **M4** — Qwen3 (`Qwen/Qwen3-0.6B`) greedy decode end-to-end; native
77+
`put_slice` for KV-cache.
78+
- **M5**`Emily.Compiler` (`Nx.Defn.Compiler`): validates opts, pins
79+
the result backend, delegates the walk to `Nx.Defn.Evaluator`.
80+
- **M6****dropped** after Phase-1 de-risk.
81+
`mlx::core::compile` wrapping measured <1.20× on transformer-shaped
82+
workloads (regression on CPU) and was cut. Microbench harness
83+
retained at `bench/native/compile_microbench.cpp` /
84+
`mix bench.native` so the decision can be re-measured against
85+
future MLX releases. Full results:
86+
[`bench/compile_microbench.md`](bench/compile_microbench.md).
87+
- **M7** — Bumblebee conformance breadth. ViT
88+
(`google/vit-base-patch16-224`) and Whisper (`openai/whisper-tiny`)
89+
each ship tiny-random (`@moduletag :conformance`) and
90+
full-checkpoint (`@moduletag :vit_full` / `:whisper_full`) tiers.
91+
`mix test --only conformance` now aggregates 14 tiny-random tests
92+
across DistilBERT, Qwen3, ViT, and Whisper.
93+
94+
## Testing
95+
96+
```bash
97+
mix test # fast suite (unit + property)
98+
mix test --only conformance # + Bumblebee tiny-random suites
99+
mix test --only qwen3_full # full Qwen3-0.6B checkpoint (~1.5 GB)
100+
mix test --only vit_full # full ViT-base (~330 MB)
101+
mix test --only whisper_full # full whisper-tiny (~150 MB)
102+
mix test --only soak # memory + concurrency soak harnesses
29103
```
30104

31-
Full `Nx.Backend` support lands in M2; see the plan.
105+
Each layer has its own oracle: hand-computed expected values at the
106+
Native layer, `Nx.BinaryBackend` on the same inputs at the Backend
107+
layer, `Emily.Backend` in non-defn mode at the Compiler layer, and
108+
HuggingFace Transformers (or EXLA) reference slices end-to-end. A bug
109+
can only be introduced in the layer where its test fails.
32110

33111
## License
34112

0 commit comments

Comments
 (0)