22
33Elixir 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
1115independently testable and avoids the [ nif_call-deadlock
1216class] ( 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