Views
No views yet
com.google.ai.edge.litert:litert), and litert-torch, the renamed ai-edge-torch, is its PyTorch converter: a PyTorch model converted unmodified with litert_torch.convert matched the original to 4e-7 on a Galaxy S26 (measured, LiteRT 2.2.0, Android 16, 2026-09-05)..tflite graphs; the host does only tokenization, the FlowMatch-Euler loop, and latent unpatchify (generate.py in this repo, ~150 lines, no torch and no diffusers).| graph | file | recipe | size |
|---|---|---|---|
| DiT (Flux2Transformer2DModel, 3.88 B) | dit_int4b32.tflite | int4 block-32 | 2.11 GiB |
| DiT — GPU-shaped export (Apple GPU / Metal) | dit_gpu_int4b32.tflite | int4 block-32, same weights | 2.11 GiB |
| text encoder (Qwen3-4B, top 9 layers pruned) | textenc_int4.tflite | int4 block-128 DRQ | 1.68 GiB |
| text encoder — higher-fidelity variant | textenc_int8_weightonly.tflite | int8 weight-only | 2.91 GiB |
| VAE decoder (AutoencoderKLFlux2) | vae_dec_fp32.tflite | fp32 | 0.19 GiB |

1pip install ai-edge-litert numpy pillow transformers
2python generate.py --model-dir . --prompt "a red fox sitting in fresh snow at sunrise" --seed 42 --out fox.pngmodels/bonsai/bonsai_image_4b).
| Device | Backend | Text encoder | DiT (per step) | VAE | Total per image | Peak memory |
|---|---|---|---|---|---|---|
| Apple-silicon Mac | CPU, 8 threads | 2.6 s | 3.9 s | 1.3 s | ~19 s | — |
| Apple-silicon Mac | GPU (Metal, fp32) — DiT only | on CPU | 0.74 s | on CPU | ~6 s steady state | ~22 GB |
| iPhone 17 Pro | CPU/XNNPACK, 6 threads | 1.8 s | 13 s | 3.1 s | ~64 s | ~2.9 GiB |
| Pixel 8a (8 GB) | CPU | — | — | — | ~7–8 min | — |
dit_gpu_int4b32.tflite and needs fp32 GPU precision (gpu_options TOML precision = 2) — the default fp16 overflows this DiT's activation range. It also pays a one-time ~40 s Metal compile per launch, which the steady-state figure excludes, and its fp32-resident weights are what put memory near 22 GB, so a 32 GB+ Mac is the practical target. The CPU-shaped dit_int4b32.tflite does not run on the Metal accelerator at all (rank-5 rotary tensors).dit_gpu_int4b32.tflite — neither accelerator produced a usable row on the S26. NPU: the ahead-of-time compile for SM8850 failed on the host. GPU: the benchmark process was killed, most likely out of memory.dit_int4b32.tflite — neither accelerator produced a usable row on the S26. NPU: the ahead-of-time compile for SM8850 failed on the host. GPU: the benchmark process was killed, most likely out of memory.textenc_int4.tflite — neither accelerator produced a usable row on the S26. NPU: the ahead-of-time compile for SM8850 failed on the host. GPU: the benchmark process was killed, most likely out of memory.textenc_int8_weightonly.tflite — neither accelerator produced a usable row on the S26. NPU: the ahead-of-time compile for SM8850 failed on the host. GPU: the benchmark process was killed, most likely out of memory.vae_dec_fp32.tflite — neither accelerator produced a usable row on the S26. NPU: the graph compiles and then fails to run (LiteRtException: Failed to invoke the compiled model). GPU: LiteRtException: Failed to compile model.BonsaiApp for iOS, BonsaiAppAndroid for Android). Measured: iPhone 17 Pro ~62 s per image; Pixel 8a (8 GB RAM) completes at ~7–8 min per image, so treat 8 GB as the floor and 12 GB+ as the practical target.dit_gpu_int4b32.tflite is the same DiT re-exported GPU-clean — rope tables precomputed with the pipeline's constant position ids and the interleaved rotation rewritten rank-4, same weights, verified numerically identical at export. On an Apple-Silicon Mac the LiteRT Metal accelerator runs it at ~0.74 s/DiT-step, ~6 s per 512×512 image steady-state (one-time ~40 s Metal compile per launch). Force fp32 GPU precision (gpu_options TOML precision = 2) — default fp16 overflows this DiT's activation range. A complete macOS SwiftUI app (DiT on Metal, text encoder + VAE on CPU) is BonsaiAppMac in the same device directory linked above; the fp32-resident GPU weights put steady-state memory around 22 GB, so a 32 GB+ Mac is recommended. The CPU-shaped dit_int4b32.tflite does not run on the Metal accelerator (rank-5 rotary tensors), so pick the file that matches your target.1// build.gradle: implementation("com.google.ai.edge.litert:litert:2.1.3")
2import org.tensorflow.lite.Interpreter
3import java.io.File
4
5fun graph(path: String, threads: Int = 6) =
6 Interpreter(File(path), Interpreter.Options().apply { // File path => the 2.11 GiB DiT is mmap'd, not copied
7 numThreads = threads
8 setUseXNNPACK(true) // required: reference kernels are both slow and wrong for blockwise int4
9 })
10
11// Stage the three graphs SEQUENTIALLY (close() each before opening the next) so
12// peak memory stays ~DiT-sized (~2.9 GiB) instead of the ~4 GiB sum:
13// 1. textenc: ids (1,256) i32, mask (1,256) i32 -> embeds (1,256,7680) f32
14// 2. DiT x N: lat (1,1024,128), embeds, sigma (1,), img_ids (1024,4), txt_ids (256,4) -> velocity
15// lat += (sigma[k+1] - sigma[k]) * velocity
16// 3. VAE: z (1,32,64,64) f32 -> image (1,3,512,512) f32, clamp(y/2+0.5)
17// Map inputs by tensor NAME (serving_default_args_<n>), never by shape — two inputs share (1,256).tfl.pow, and blockwise zero-scales in all-zero blocks need patching for XNNPACK). The text encoder is exported as a prompt embedder — the pipeline reads hidden states from layers (9, 18, 27) only, so the top 9 of 36 layers and the LM head are pruned for free.