xocialize commited on
Commit
851cff7
Β·
verified Β·
1 Parent(s): af91a6b

Port complete on both consumers; add measured memory guidance and DFR/multishot notes

Browse files
Files changed (1) hide show
  1. README.md +66 -4
README.md CHANGED
@@ -19,10 +19,15 @@ MLX-format conversion of [Lightricks LTX-2.5](https://huggingface.co/Lightricks/
19
  (joint audio+video DiT, 22B) for Apple Silicon, in the per-component split layout consumed by
20
  [`ltx-2-mlx`](https://github.com/xocialize/ltx-2-mlx) (branch `ltx-2.5`).
21
 
22
- > **Status: research port in progress.** The conversion layer is verified (see *Conversion
23
- > receipts*); the MLX pipeline generates end-to-end and its text-encoder path is parity-gated
24
- > against the PyTorch reference. Remaining parity gates (DiT forward, e2e latents) are being
25
- > landed on the consuming repo's `ltx-2.5` branch.
 
 
 
 
 
26
 
27
  ## ⚠️ License β€” read before use
28
 
@@ -83,6 +88,39 @@ numerically altered beyond layout/serialization transforms. Conversion tooling:
83
  identical; all 49 tapped hidden states β‰₯ 0.9997 cosine; pre-connector projections β‰₯ 0.99996.
84
  - The embedded LTX-2.x license text present in upstream file metadata is preserved.
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  ## Usage
87
 
88
  ```bash
@@ -95,6 +133,30 @@ uv run ltx-2-mlx generate \
95
  -H 512 -W 768 -f 121 --frame-rate 24 -o fox.mp4
96
  ```
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  ## Provenance
99
 
100
  Converted from [`Lightricks/LTX-2.5`](https://huggingface.co/Lightricks/LTX-2.5) (comfy split
 
19
  (joint audio+video DiT, 22B) for Apple Silicon, in the per-component split layout consumed by
20
  [`ltx-2-mlx`](https://github.com/xocialize/ltx-2-mlx) (branch `ltx-2.5`).
21
 
22
+ > **Status (2026-08-13): the port is COMPLETE on both consumers.** Python-MLX
23
+ > ([`ltx-2-mlx`](https://github.com/xocialize/ltx-2-mlx), branch `ltx-2.5`) and Swift-MLX
24
+ > ([`ltx-2-mlx-swift`](https://github.com/xocialize/ltx-2-mlx-swift)) both generate end to end,
25
+ > including the DFR pipeline with temporal rounds. Parity-gated per component against the
26
+ > PyTorch reference: text encoder 49 states (mean cosine 0.999985), DiT forward, sampler,
27
+ > keyframe slots, and the DFR canvas geometry bit-exactly.
28
+ >
29
+ > Still a **research port** β€” it is not a supported product, and see *Memory* below before you
30
+ > plan a run.
31
 
32
  ## ⚠️ License β€” read before use
33
 
 
88
  identical; all 49 tapped hidden states β‰₯ 0.9997 cosine; pre-connector projections β‰₯ 0.99996.
89
  - The embedded LTX-2.x license text present in upstream file metadata is preserved.
90
 
91
+ ## Memory β€” read this before planning a run
92
+
93
+ Measured on Apple Silicon (128 GB unified), 448Γ—320Γ—9 frames, bf16, via the Swift consumer.
94
+ Your numbers will differ with resolution and frame count; the *structure* is what transfers.
95
+
96
+ | | peak |
97
+ |---|---|
98
+ | default (text encoder co-resident with the DiT) | **62.40 GB** |
99
+ | with the DiT evicted around the encode phase | **40.66 GB** |
100
+
101
+ **The text encoder is the surprise.** `gemma4-12b-ltx-v1/` is an unquantized **bf16 12B β€”
102
+ 23.8 GB on disk, ~24.4 GB resident**. LTX-2.3 used a 4-bit Gemma-3 (~7 GB), so anything you
103
+ carry over from a 2.3 setup will badly under-estimate 2.5. The DiT itself is a 37.98 GB
104
+ resident floor at bf16.
105
+
106
+ Two levers, and they stack:
107
+
108
+ 1. **Evict the DiT around the phases that don't need it.** βˆ’21.74 GB (βˆ’34.8%) with output
109
+ **bit-identical** and wall-clock within run-to-run noise. This is a scheduling change, not
110
+ a quality trade β€” it is on by default in the Swift consumer.
111
+ 2. **Quantize the text encoder to int8** (group 64, keeping `embed_tokens` in bf16): encoder
112
+ 24.42 β†’ 14.20 GB, end-to-end 62.40 β†’ 52.18 GB. We measured this as **numerically faithful**
113
+ (valid-token cosine 0.999820 against a 0.999879 bf16 floor) and **perceptually neutral** in a
114
+ blinded 6-pair operator A/B (3 ties, 2–1, all "very close").
115
+ ⚠️ **int4 was REJECTED** β€” 0.996728 on the same metric, consistent with an independent
116
+ in-fleet measurement of a different frozen encoder. Note that several third-party MLX packs
117
+ ship this encoder at 4-bit with no published quality data.
118
+ **No quantized sibling is published here**; the recipe is in the consuming repo.
119
+
120
+ ⚠️ **If you quantize it yourself:** the default `mlx_lm.convert -q` also quantizes
121
+ `embed_tokens`, which is hidden state 00 *and* the input to all 48 layers β€” exclude it. The
122
+ `mixed_*` recipes are worse: with tied embeddings they put the embedding table at 3 bits.
123
+
124
  ## Usage
125
 
126
  ```bash
 
133
  -H 512 -W 768 -f 121 --frame-rate 24 -o fox.mp4
134
  ```
135
 
136
+ **Multishot (LTX-2.5 generated keyframe slots)** β€” `--num-generated-keyframes N` places N
137
+ invented keyframes at evenly spaced interior positions. Note what this does and does not do:
138
+ it relaxes the effective temporal compression at those positions (each slot costs a full
139
+ latent frame of tokens to buy one pixel frame). In our measurement it did **not**, on its own,
140
+ turn a shot-listed prompt into multiple cut shots β€” that is the DFR pipeline's job.
141
+
142
+ **DFR (diffusion fidelity rendering)** is implemented in both consumers. Its *spatial*
143
+ detailing pass was preferred by an operator on 4 of 4 matched pairs for roughly Γ—1.2 time.
144
+ Its *temporal rounds* are a parity/quality feature, **not** a speed one: they cost Γ—2.767
145
+ (1 round) / Γ—3.542 (2 rounds), and generating the same deliverable natively at the target
146
+ frame rate was both ~2.8Γ— cheaper and preferred. Use rounds when you want the temporal
147
+ density, not to save time.
148
+
149
+ ### Swift
150
+
151
+ ```swift
152
+ // https://github.com/xocialize/ltx-2-mlx-swift β€” MLX-Swift consumer.
153
+ // 2.5 is detected from the CHECKPOINT (the in-dir gemma4-12b-ltx-v1/), never a path name,
154
+ // so a renamed or relocated copy still resolves correctly.
155
+ let pipeline = try await LTX2Pipeline.load(ltxDir: modelDir, gemmaDir: gemma4Dir)
156
+ let out = try await pipeline.t2vTwoStage(prompt: prompt, height: 320, width: 448,
157
+ numFrames: 25, fps: 24, seed: 4242)
158
+ ```
159
+
160
  ## Provenance
161
 
162
  Converted from [`Lightricks/LTX-2.5`](https://huggingface.co/Lightricks/LTX-2.5) (comfy split