Repository navigation
Conversation
|
@jschmied, we ran GLM's checks on this branch at c48be49 on our three GB10 hosts, with the GLM-5.3 NVFP4 checkpoint, and they all pass:
So the Ling commits leave GLM's checks and replies as they were. We have no Ling weights here, so this covers GLM only. |
|
Thank you, @Bizuayeu, that's the check we couldn't run. We'll push three more pieces to this branch shortly: serving at TP=1 (engine, lane backend, kept prompt states), the MTP drafter ( A rerun once it's up would be welcome, if you have the time. |
|
Pushed |
|
Thank you, @jschmied, for the GLM TP=2/3 checks and for identifying the upcoming serving, MTP and packer changes separately. The routing comparison needs care: At c48be49, the zero-limit GLM behavior and the 16-row helper's arbitrary-resume behavior remain open. Please send the pushed serving/MTP head and its delta from c48be49 so we can recheck those paths and the unchanged-shared-kernel claim. Posted by Ash's AI Assistant Synthena |
|
Thank you. Both open items are closed at
Delta from
Checked at |
|
Thank you, @jschmied, for restoring GLM's zero limit behavior and removing the 16 row WY path. We checked e29e0c2. Please send a focused revision covering these cases, with geometry rejection and allocation failure regressions, so we can recheck it. Posted by Ash's AI Assistant Synthena |
|
@jschmied, we ran GLM's checks again at e29e0c2 on our three GB10 hosts, with the GLM-5.3 NVFP4 checkpoint, and they all pass:
We had also run ac58df6 the same way, with the same results. As before, this covers GLM only. |
|
@ashhart, thank you. @Bizuayeu, thanks for rerunning the GLM checks at e29e0c2. A focused revision on top of 1. Geometry rejected at config load (
Regression ( 2. Regression: 3. Capacity refusal before KDA commits ( Regression ( 4. Singleton routing groups ( Regression: the refusals and the four accepted edge cases in 5. Engine init cleanup ( Regression: 6. Undefined slot read in Regression: 7. Full checkpoint and HF source
Re-run at
|
|
Thank you, @jschmied, for the focused fixes and regression cases at 7c44606. The config refusals, Store ownership, normal sequence-capacity checks, slot initialization and engine cleanup now address the earlier cases in source. GLM's zero-limit behavior remains restored and WY remains removed. Two follow-ups remain. Please add the scratch preflight and drain-before-free error path, with a mismatched-capacity case and a test that keeps a transfer pending. We can fold these small fixes in at landing. We'll compare the newly pinned HF sources and run the host/fault checks, followed by full-model drafted/plain, resumed/fresh and concurrent/solo tests with arbitrary cuts and partial keeps. Posted by Ash's AI Assistant Synthena |
|
@ashhart, thank you. Both follow-ups are in, one commit each, on top of 1. Scratch preflight ( Regression ( 2. Drain before free (
If the drain itself fails, the memory stays allocated on purpose. Regression: Re-run at
The branch conflicts with main since 1.0.12 (#804 is in it). I can rebase it onto main if that helps the recheck. |
…layer shared SwiGLU limit, GLM's bytes unchanged tf_glm_topk takes n_group and topk_group: with more than one group it keeps the topk_group groups of the best top-2 biased sums (lower group on ties) before the top-k. GLM's one group skips that path. swiglu's limit 0 now means unclamped (as the experts' 0), and the MoE layer carries the shared expert's limit apart from the routed one's. Tests: glm-ref adds group routing vs float64 and the swiglu limit; glm-route-old compares GLM's select and swiglu with 6380c8d's fatbin; glm-route-ling checks captured Ling layer-4 routing against HF and exllamav3.
…SwiGLU limits as GLM's kernels take them config.zig parses config.json (BailingMoeV3ForCausalLM, bailing_hybrid) into a typed Config: widths, the MLA/KDA schedule ((i+1) % layer_group_size, tail MLA), dense layers, KDA safe gate, MLA head-wise gate and GPT-J rope, sigmoid routing with 8 groups keeping 4, per-layer expert and shared SwiGLU limits, one MLA MTP layer, FP8 128x128 blocks and modules_to_not_convert. It refuses what the engine does not run, each with a one-sentence reason. names.zig enumerates every tensor the config implies by role, with FP8 scale companions; against the checkpoint's index (fixtures/names.txt, folded, count and sha256 checked) it finds all 214,025 names, none unaccounted: 210,925 backbone and 3,100 of the MTP layer 72. cuda.zig gives select()'s Groups and each layer's limits for moe.Layer. Host tests: zig build test-ling (also under test-cuda-host).
…Opt NVFP4) at TP=1, synthetic checkpoints in each config.zig: Quant gains nvfp4. quantization_config fp8 (128x128) or modelopt NVFP4 (groups of 16), or hf_quant_config.json's block (parseWith); ignore / exclude_modules / modules_to_not_convert as names, dotted tails or * globs; format(module) gives each linear's format; exl3 refused naming ashhart#832. names.zig emits each format's side tensors and moduleName() gives the module a role reads. weights.zig / weights_proj.zig: one Layer per layer with a qlinear.Weight per projection (bf16, fp8g, nvfp4 through modelopt.Packer and fp8.packScales), routed experts as experts.Layer (Fp8Experts / Nvfp4Experts packers; bf16 as lane weights, no grouped bf16 kernel), the shared expert as projections, norms, fp32 q|k|v convs, A_log, dt_bias, bf16 router (a quantized one refused) and bias, the MTP layer's tensors. A file tensor whose dtype or shape disagrees with the config is refused. Tests: test-ling (21, with TF_LING_INDEX on the real index); ling-synth writes 4-layer + MTP checkpoints with the real names (bf16 282, FP8 517, NVFP4 978 tensors, NVFP4 mixing a bf16 dense layer); ling-weights: every projection decode and prompt vs float64 from the file, worst 0.456 of the bound, experts' gate-up and down, 37 small tensors byte-equal in each format; ling-weights-real: layers 0, 4, 7 with 4 experts from the FP8 release vs torch (oracle/ling_weights.py), 35 products, worst 0.414 of the bound, rel L2 <= 1.7e-3. Mutations caught: gate/up swapped, conv order, NVFP4 global dropped, exceptions ignored, expert down scales misplaced.
…thout the checkpoint, memory per format
…e heads, head-wise gate chunks_wide<HB, RW>: q and cache rows LW + RW wide, scores over all of them, values from the 512 latent; tf_glm_chunks_r16 is the 576-wide 16-head form (RW 0 builds GLM's kernels to the same SASS). absorb_q/expand_v take the head width as a template (GLM 256, Ling 128; the 128 absorb reads q_nope at a q stride, writes qa at a stride). New tf_glm_rope (GPT-J pairs, fp32 angles, sincosf) and tf_glm_head_gate (fp32 sigmoid). Host: absorbQStrided, expandV at 128, rope, ropeFreqs, headGate, attentionRoped. Checks: ling-mla (float64 bounds; decode rows, a verify window and a cut prompt across key 1536 equal the uncut call), ling-mla-real (layer 7 vs HF and float64; 400 + 32 decode rows, an 8-row window and a cut at 300 equal the prompt), glm-mla-same (GLM's MLA launches against a 6380c8d glm_glue fatbin, byte for byte).
…opt-in 16-row WY chunked prompt path - glm-kda-ling: glm_kda's fused chain, 16-row windows, wide prompt and chunked prompt on Ling-3.1 layer 0 (q/k/v convs as one q|k|v conv): rel L2 3.6e-3 vs fla, within float64's bound; replays == chain bits. - glm_kda_chunk.cu: new entry tf_glm_kda_chunk over prep's saves (kda.chunked, kda.chunkReplay). Opt-in only: GLM stays on the row path; glm_kda.cu and its fatbin are unchanged. Not the row path's bits; cuts at multiples of 16 == uncut, a prompt resumed elsewhere differs from a fresh one (measured in glm-kda-chunk). - glm-kda-chunk: float64 bounds end to end and from the saves, saves equal the wide chain's, resumed vs fresh. - glm-kda-speed: 64 heads x 128, 2048-row prompt row path vs chunked, decode row, 16-row window.
…dense and MoE MLPs, head, windows and commits Every projection runs as the qlinear.Weight and every routed-expert layer as the experts.Layer the checkpoint loaded (bf16, FP8 block, NVFP4); MLA's absorbed kv_b is derived through kv_b's own format on identity rows. ling_glue.cu rounds the residual, head gate and expert sum where the HF bf16 model does. Decode windows run KDA's three-kernel chain as GLM's forward does; commit(keep) replays the KDA states of a partial window. ling-forward-synth: synthetic bf16/FP8/NVFP4 logits vs a float64 reference routed on the same picks (2.1-2.3e-2, as far as HF's bf16 rounding points are), cut == uncut, windows == decode rows, a partial commit, batched == alone, fused == three-kernel chain, bit for bit. ling-layer-real: real FP8 layers 0, 4, 7, 71 vs HF 3.0-3.7e-3 without routing flips. GLM files untouched.
Ling's reference (inclusionAI's SwigluStepAndMul, SGLang's BailingMoeV3MLP) clamps silu's output; GLM's glue and the grouped expert kernels clamp the gate before silu. Ling now launches its own instances: tf_ling_swiglu_512/64 in glm_glue (glue.swigluWith) for the shared, dense and bf16 routed experts, and epilogue 4 of the FP8 and NVFP4 grouped kernels, decode and prompt, chosen by experts.Layer.after (set by the Ling loader, refused by affine4). Every pre-existing kernel keeps its SASS and resource usage (cuobjdump over all 40 fatbins vs a33dde0; 37 fatbins byte-identical, the other 3 only add the new instances). ling-clamp-old runs the kept FP8 (12) and NVFP4 (13) instance calls against a33dde0's fatbins byte for byte; glm-route-old and glm-mla-same still equal 6380c8d. At L = 10 both orders give the same bf16 bytes (silu is rounded to bf16 first and bf16(silu(g)) = 10 for g >= 10), so the real layers 65 and 71 and the synthetic forward measure the same digits; ling-clamp checks the order against float64 at L = 10 and L = 2 (where 16,255 bf16 gates differ) and catches 5 of 5 mutants. The float64 oracle clamps after silu too.
…ver hooks and kept prompt states Engine: a sequence per slot, prompt chunks cut at any row (marks included), verify windows of up to 16 rows, shared windows of up to 8 streams, greedy (tf_glm_top1) and sampled (sample.cu) draws, kept states as arena copies. Lanes: GLM's cuda_lanes.zig without the ranks. native.zig registers bailing_hybrid with the CUDA server. ling-lanes checks chunked == whole, drafted == plain, resumed == fresh (marks off multiples of 16), concurrent == solo, in tokens and state bytes, and the served logits against ling_forward.py's float64 on the same picks.
…h_proj, MLA + MoE on its own cache, chains of 3 mtp.Head runs the MTP layer over a view of the weights (final_layernorm as its norm, the shared head); each sequence keeps a second State for its MLA cache. Prompt passes feed every row whose next token is known, Engine.draft absorbs a verify's kept rows and chains drafts on the layer's own final-norm rows, kept states copy the MTP cache. ling-lanes --mtp: MTP-drafted == plain (greedy, sampled, resumed, concurrent), chained drafts by hand, MTP logits vs ling_forward.py --mtp float64; ling-mtp-real: the real layer vs HF's BailingMoeV3MTPLayer (oracle/ling_mtp.py).
… and checks its bytes against the host packers; ling-layer-real can dump its rows
…ed on the GPU (Packer.fp8Experts, fp4Experts), the file's pages dropped per batch
…hase; ling-layer-bytes writes every path's bytes for cmp
…P column copies -50 %), the same bytes
… no copies (4 KDA, 2 per MLP), the same bytes
…e expert kernels (same bits, -7 to -27 % expert time up to 768 rows)
…ne columns (Ling's 64), same picks and weights; ling-route-old
…dle GPU, the expert kernels and plan tiles side by side
|
Rebased onto main |
…ent check counts, the recorder on main
|
We are pausing here. The branch stays as it is at Why: the whole model does not fit one GB10. FP8 is 574 GB and NVFP4 about 324 GB, so an end-to-end run, real MTP acceptance and TP all need several Sparks; this PR was checked on synthetic checkpoints and single real layers only. A one-Spark build would have to be around 1.25-1.7 bpw EXL3, which TensorFold reads only once #832 is in. We tried our own 1.25 bpw conversion and stopped it: with 1-bit experts, each layer's error rose about 1.5x over 1.5-bit experts and the hidden states drifted quickly with depth, so we didn't expect a usable model. Next steps: someone with two or more Sparks can take TP and the end-to-end checks ( |
|
Thank you, @jschmied, the scratch and failure controls leave useful work available even though the whole model exceeds one GB10. We checked e99e650 and will keep the branch parked, following your pause. It stays parked until a quality-qualified pack fits one GB10 or the TP fabric in #636 lands, with resumption left to you. The failed-drain teardown follow-up at engine.zig:149-163/199-204 can go in at landing. We will retain your synthetic, allocation and real-layer receipts with their scope; whole-model, real MTP and TP qualification still need their own runs when work resumes. Posted by Ash's AI Assistant Synthena |
|
Ling-3.0-tiny ( What 3.0-tiny changes in the config, one by one
Receipts (one GB10, bf16, files checked by
One test threshold changed: Not covered: the official FP8 tiny on real weights (the loader's FP8 path is checked on synthetic tiny-shaped files), EXL3, TP > 1, MTP (tiny has none). The server needs the possessive-quantifier tokenizer change that main already has ( |
ashhart
left a comment
There was a problem hiding this comment.
Thanks @jschmied. The exactness work here is strong (float64 references, oracles pinned by sha256 to the HF source, fault injection, weights served in their own formats). It's about 1,800 commits behind (base beta.7) with 13 conflicts in the shared GLM CUDA kernels, so it needs a rebase onto current main, after which GLM's images, launch records and tokens must be shown unchanged. Please also register tf_glm_chunks_r16, tf_pack_fp8_experts, ling_glue and the EPI-4 instances with the kernel contract. Then we'd run the Ling oracles, drafted vs drafts-off, and a GLM token check on a GB10 at TP=1.
|
Review against main e9b69ed. The exactness work here is strong: float64 references, oracles pinned by SHA-256 to the Hugging Face source, fault-injection tests, and weights served in the checkpoint's own formats. The blocker is age: the branch is based on beta.7, about 1,800 commits behind, with 13 conflicts in shared GLM CUDA kernels. The GLM changes use template defaults and new instances ( |
|
Thanks @jschmied for Ling on CUDA and the careful float64 references. We're closing this draft for now, as it has been parked since 16 Oct and our 26 Oct rebase request hasn't had a reply. You're very welcome to reopen it, or send a fresh PR for Ling-3.0-tiny, rebased on current main with GLM's images and tokens shown unchanged. |
Config parse, weight map, fixtures, synth JSON, and the float64 oracle as in jschmied/TensorFold ling-tiny 98026d3. Host-only subset: no CUDA kernels from the closed ashhart#874 draft. Source: https://github.com/jschmied/TensorFold/tree/ling-tiny
Config parse, weight map, fixtures, synth JSON, and the float64 oracle as in jschmied/TensorFold ling-tiny 98026d3. Host-only subset: no CUDA kernels from the closed ashhart#874 draft. Source: https://github.com/jschmied/TensorFold/tree/ling-tiny
Config parse, weight map, fixtures, synth JSON, and the float64 oracle as in jschmied/TensorFold ling-tiny 98026d3. Host-only subset: no CUDA kernels from the closed #874 draft. Source: https://github.com/jschmied/TensorFold/tree/ling-tiny
…aunch kda.geometry(key_dim, value_dim, conv_taps) returns error.KdaGeometry unless the model's heads are the kernels' 128 keys and 128 values and its conv window has kda.cuh's 4 taps, so a family calls it at load, before any allocation or launch (ashhart#874's review: unsupported head widths and tap counts refused up front). kda.taps names TAPS beside dk and dv. No kernel or launch changes; GLM's bytes are untouched. Part of ashhart#850, stacked on ashhart#2142.
…nks, the native 262,144 window) inclusionAI's Ling-3.1-flash (bailing_moe_v3: 63 KDA and 9 MLA layers, 512 routed experts with group-limited sigmoid routing) on the native CUDA server, built on ashhart#874's TP=1 port and ashhart#1541's host package (config, names, route, oracle, synth, fixtures). ashhart#850 step 5, on the shared pieces: - KDA layers on the shared CUDA KDA (ashhart#2142's cuda.kda: chain, saved-row replay through replayLayers); config.fixed is held to kda.geometry in a test. - Routing on moe_glue's group-limited score form (tf_moe_topk_sigmoid_score_groups). - MLA on the shared mla image (cuda.mla: 576-wide chunk tiles, 128-wide absorb and expand, the plain rope, the head-wise output gate), merged by GLM's latent merge; prompt rows in the expanded form on the family's tf_ling_pattn. - EXL3 packs through main's shared EXL3 operators (cuda.exl3 Dense and tiles, exl3_experts with tf_exl3_plan_finish_after for Ling's clamp order, exl3_grouped). bf16 checkpoints load for the synthetic checks; FP8 and NVFP4 checkpoints are refused by name (LingFormat). - The dense shared expert's SwiGLU in Ling's clamp order: ling_act.cu instantiates glm_swiglu.cuh's rows with the clamp on silu's output. - TP=2 over hosts (TF_LING_RANK, TF_LING_WORLD): heads, expert widths and the head's vocabulary cut on EXL3's 128-wide blocks; rank 1 follows rank 0's steps; RoCE gathers up to GLM's cap. - Lanes: shared windows across streams, copy drafts, CUDA graphs for one sequence's verify windows, prompt states kept in memory for the prompt cache. - Admission: --context up to the native 262,144 positions (LingContext past it); config rope_scaling is refused as main's config does; the MTP layer is not loaded (drafts come from the prompt's text). - Checks in tf-cuda-test (ling-route, ling-mla, ling-clamp, ling-synth, ling-weights, ling-forward-synth, ling-lanes, ling-init-faults, ling-exl3-layers, ling-exl3-clamp, ling-tp, ling-ckpt-check, ling-slots), test-ling on the host, docs/recipes/ling-3.1-flash-cuda.md. Wiring: the ling_glue and ling_act images, the engine in cuda_engines.zig, the native registry entry, test-ling and the runner's ling_cuda import. GLM: glm/cuda_latent.merge is public (Ling's chunks merge through tf_glm_merge). The family's fake driver reports a discrete device, so downloads in its host tests copy directly instead of through main's per-thread pinned bounce. Co-authored-by: Jürgen Schmied <600316+jschmied@users.noreply.github.com>
…aunch kda.geometry(key_dim, value_dim, conv_taps) returns error.KdaGeometry unless the model's heads are the kernels' 128 keys and 128 values and its conv window has kda.cuh's 4 taps, so a family calls it at load, before any allocation or launch (ashhart#874's review: unsupported head widths and tap counts refused up front). kda.taps names TAPS beside dk and dv. No kernel or launch changes; GLM's bytes are untouched. Part of ashhart#850, stacked on ashhart#2142.
…aunch kda.geometry(key_dim, value_dim, conv_taps) returns error.KdaGeometry unless the model's heads are the kernels' 128 keys and 128 values and its conv window has kda.cuh's 4 taps, so a family calls it at load, before any allocation or launch (ashhart#874's review: unsupported head widths and tap counts refused up front). kda.taps names TAPS beside dk and dv. No kernel or launch changes; GLM's bytes are untouched. Part of ashhart#850, stacked on ashhart#2142.
…nks, the native 262,144 window) inclusionAI's Ling-3.1-flash (bailing_moe_v3: 63 KDA and 9 MLA layers, 512 routed experts with group-limited sigmoid routing) on the native CUDA server, built on ashhart#874's TP=1 port and ashhart#1541's host package (config, names, route, oracle, synth, fixtures). ashhart#850 step 5, on the shared pieces: - KDA layers on the shared CUDA KDA (ashhart#2142's cuda.kda: chain, saved-row replay through replayLayers); config.fixed is held to kda.geometry in a test. - Routing on moe_glue's group-limited score form (tf_moe_topk_sigmoid_score_groups). - MLA on the shared mla image (cuda.mla: 576-wide chunk tiles, 128-wide absorb and expand, the plain rope, the head-wise output gate), merged by GLM's latent merge; prompt rows in the expanded form on the family's tf_ling_pattn. - EXL3 packs through main's shared EXL3 operators (cuda.exl3 Dense and tiles, exl3_experts with tf_exl3_plan_finish_after for Ling's clamp order, exl3_grouped). bf16 checkpoints load for the synthetic checks; FP8 and NVFP4 checkpoints are refused by name (LingFormat). - The dense shared expert's SwiGLU in Ling's clamp order: ling_act.cu instantiates glm_swiglu.cuh's rows with the clamp on silu's output. - TP=2 over hosts (TF_LING_RANK, TF_LING_WORLD): heads, expert widths and the head's vocabulary cut on EXL3's 128-wide blocks; rank 1 follows rank 0's steps; RoCE gathers up to GLM's cap. - Lanes: shared windows across streams, copy drafts, CUDA graphs for one sequence's verify windows, prompt states kept in memory for the prompt cache. - Admission: --context up to the native 262,144 positions (LingContext past it); config rope_scaling is refused as main's config does; the MTP layer is not loaded (drafts come from the prompt's text). - Checks in tf-cuda-test (ling-route, ling-mla, ling-clamp, ling-synth, ling-weights, ling-forward-synth, ling-lanes, ling-init-faults, ling-exl3-layers, ling-exl3-clamp, ling-tp, ling-ckpt-check, ling-slots), test-ling on the host, docs/recipes/ling-3.1-flash-cuda.md. Wiring: the ling_glue and ling_act images, the engine in cuda_engines.zig, the native registry entry, test-ling and the runner's ling_cuda import. GLM: glm/cuda_latent.merge is public (Ling's chunks merge through tf_glm_merge). The family's fake driver reports a discrete device, so downloads in its host tests copy directly instead of through main's per-thread pinned bounce. Co-authored-by: Jürgen Schmied <600316+jschmied@users.noreply.github.com>
…aunch kda.geometry(key_dim, value_dim, conv_taps) returns error.KdaGeometry unless the model's heads are the kernels' 128 keys and 128 values and its conv window has kda.cuh's 4 taps, so a family calls it at load, before any allocation or launch (ashhart#874's review: unsupported head widths and tap counts refused up front). kda.taps names TAPS beside dk and dv. No kernel or launch changes; GLM's bytes are untouched. Part of ashhart#850, stacked on ashhart#2142.
…aunch kda.geometry(key_dim, value_dim, conv_taps) returns error.KdaGeometry unless the model's heads are the kernels' 128 keys and 128 values and its conv window has kda.cuh's 4 taps, so a family calls it at load, before any allocation or launch (#874's review: unsupported head widths and tap counts refused up front). kda.taps names TAPS beside dk and dv. No kernel or launch changes; GLM's bytes are untouched. Part of #850, stacked on #2142.
…nks, the native 262,144 window) inclusionAI's Ling-3.1-flash (bailing_moe_v3: 63 KDA and 9 MLA layers, 512 routed experts with group-limited sigmoid routing) on the native CUDA server, built on ashhart#874's TP=1 port and ashhart#1541's host package (config, names, route, oracle, synth, fixtures). ashhart#850 step 5, on the shared pieces: - KDA layers on the shared CUDA KDA (ashhart#2142's cuda.kda: chain, saved-row replay through replayLayers); config.fixed is held to kda.geometry in a test. - Routing on moe_glue's group-limited score form (tf_moe_topk_sigmoid_score_groups). - MLA on the shared mla image (cuda.mla: 576-wide chunk tiles, 128-wide absorb and expand, the plain rope, the head-wise output gate), merged by GLM's latent merge; prompt rows in the expanded form on the family's tf_ling_pattn. - EXL3 packs through main's shared EXL3 operators (cuda.exl3 Dense and tiles, exl3_experts with tf_exl3_plan_finish_after for Ling's clamp order, exl3_grouped). bf16 checkpoints load for the synthetic checks; FP8 and NVFP4 checkpoints are refused by name (LingFormat). - The dense shared expert's SwiGLU in Ling's clamp order: ling_act.cu instantiates glm_swiglu.cuh's rows with the clamp on silu's output. - TP=2 over hosts (TF_LING_RANK, TF_LING_WORLD): heads, expert widths and the head's vocabulary cut on EXL3's 128-wide blocks; rank 1 follows rank 0's steps; RoCE gathers up to GLM's cap. - Lanes: shared windows across streams, copy drafts, CUDA graphs for one sequence's verify windows, prompt states kept in memory for the prompt cache. - Admission: --context up to the native 262,144 positions (LingContext past it); config rope_scaling is refused as main's config does; the MTP layer is not loaded (drafts come from the prompt's text). - Checks in tf-cuda-test (ling-route, ling-mla, ling-clamp, ling-synth, ling-weights, ling-forward-synth, ling-lanes, ling-init-faults, ling-exl3-layers, ling-exl3-clamp, ling-tp, ling-ckpt-check, ling-slots), test-ling on the host, docs/recipes/ling-3.1-flash-cuda.md. Wiring: the ling_glue and ling_act images, the engine in cuda_engines.zig, the native registry entry, test-ling and the runner's ling_cuda import. GLM: glm/cuda_latent.merge is public (Ling's chunks merge through tf_glm_merge). The family's fake driver reports a discrete device, so downloads in its host tests copy directly instead of through main's per-thread pinned bounce. Co-authored-by: Jürgen Schmied <600316+jschmied@users.noreply.github.com>
…nks, the native 262,144 window) inclusionAI's Ling-3.1-flash (bailing_moe_v3: 63 KDA and 9 MLA layers, 512 routed experts with group-limited sigmoid routing) on the native CUDA server, built on ashhart#874's TP=1 port and ashhart#1541's host package (config, names, route, oracle, synth, fixtures). ashhart#850 step 5, on the shared pieces: - KDA layers on the shared CUDA KDA (ashhart#2142's cuda.kda: chain, saved-row replay through replayLayers); config.fixed is held to kda.geometry in a test. - Routing on moe_glue's group-limited score form (tf_moe_topk_sigmoid_score_groups). - MLA on the shared mla image (cuda.mla: 576-wide chunk tiles, 128-wide absorb and expand, the plain rope, the head-wise output gate), merged by GLM's latent merge; prompt rows in the expanded form on the family's tf_ling_pattn. - EXL3 packs through main's shared EXL3 operators (cuda.exl3 Dense and tiles, exl3_experts with tf_exl3_plan_finish_after for Ling's clamp order, exl3_grouped). bf16 checkpoints load for the synthetic checks; FP8 and NVFP4 checkpoints are refused by name (LingFormat). - The dense shared expert's SwiGLU in Ling's clamp order: ling_act.cu instantiates glm_swiglu.cuh's rows with the clamp on silu's output. - TP=2 over hosts (TF_LING_RANK, TF_LING_WORLD): heads, expert widths and the head's vocabulary cut on EXL3's 128-wide blocks; rank 1 follows rank 0's steps; RoCE gathers up to GLM's cap. - Lanes: shared windows across streams, copy drafts, CUDA graphs for one sequence's verify windows, prompt states kept in memory for the prompt cache. - Admission: --context up to the native 262,144 positions (LingContext past it); config rope_scaling is refused as main's config does; the MTP layer is not loaded (drafts come from the prompt's text). - Checks in tf-cuda-test (ling-route, ling-mla, ling-clamp, ling-synth, ling-weights, ling-forward-synth, ling-lanes, ling-init-faults, ling-exl3-layers, ling-exl3-clamp, ling-tp, ling-ckpt-check, ling-slots), test-ling on the host, docs/recipes/ling-3.1-flash-cuda.md. Wiring: the ling_glue and ling_act images, the engine in cuda_engines.zig, the native registry entry, test-ling and the runner's ling_cuda import. GLM: glm/cuda_latent.merge is public (Ling's chunks merge through tf_glm_merge). The family's fake driver reports a discrete device, so downloads in its host tests copy directly instead of through main's per-thread pinned bounce. Co-authored-by: Jürgen Schmied <600316+jschmied@users.noreply.github.com>
What this changes
Ling-3.1-flash (#850) on CUDA at TP=1, built on #804's GLM kernels as agreed there and on #850. #804 is in main since 1.0.12, so this PR is stacked on nothing: the branch is rebased onto main
46a08c18(TensorFold 1.0.12), and every commit on it is this PR's. The family is registered with the native CUDA server (model_typebailing_hybrid) and serves through the lane core at TP=1, with the prompt cache and MTP drafts. The whole model does not fit one Spark, so serving is checked on synthetic checkpoints and a 4-layer slice of the real one.docs/recipes/ling-3.1.mdlists what exists, what is open, and how to test without the 572 GB checkpoint.f9fc1a78); a division-free branch for groups that are whole lane columns, which GLM's single group never enters (e78d23bb);swiglukeeps its limit as given, 0 included, as at6380c8da; only Ling'sswigluWithmaps 0 to unclamped (32055e9e,991383f3);7d184cb7);ef8f12f3). The opt-in 16-row chunked prompt path that commit added is removed again in03171bf5: nothing served called it, and GLM'scuda_kda.zigis6380c8da's file again;min(silu(g), L) * clip(u, -L, L), as new kernel instances (bfb44ddc);tf_pack_fp8_expertsinmodelopt_pack.cu, an added kernel for Ling's loader; the existing packers are unchanged (bbe360f9).zig/src/families/ling/, format-agnostic:a6559571);qlinearandexperts. EXL3 is refused and points to EXL3 checkpoints on the native engine (tracker) #832 (d46e3723);ling-synthfor small synthetic checkpoints in each format;commit(keep)(6f583baf);a3f14d30):Enginewith a sequence per slot, prompt chunks cut at any row, verify windows of up to 16 rows, shared windows of up to 8 streams, greedy and sampled draws;Lanes, GLM'scuda_lanes.zigwithout the ranks;native.zigwith the server hooks and the prompt cache'ssnap_*hooks. A kept state is the sequence's arena copied whole, and a mark may fall on any row;model.layers.72) as the drafter:eh_proj(enorm(embed(t+1)) | hnorm(h_t)), MLA + MoE on its own cache, chains of up to 3 (577436b0). The server loads it unless--no-drafts;bbe360f9);99652bab,b3faea69,5b006370), and timing tests (7bd7a40a,412f66fc,0ffce4f7,bd7c23e3);faf7af1c).KdaGeometry,MlaGeometry,Width,TiedHead(66579cba);Store.allocowns a buffer only once it is in both lists;fake_driver.zigfails every driver call in turn (674163fa);82b3817b);group_limited_topkcan't run them (92afed61);Engine.initfrees sequence 0 and the slot list whenbind(0)or the last sync fails (0ba2adfb);0ba510ad);names.dims(5649c63b);inclusionAI/Ling-3.1-flash@a6943f4bby sha256 (e4c9bcc5);5e931c3a);1395e267).Receipt
Environment: TensorFold
46a08c18(1.0.12) plus this branch (e99e6505; its last commit only updates the recipe, so the checks below ran at1395e267), one GB10 (sm121), CUDA 13.0, Zig 0.17.0. Checkpoint:inclusionAI/Ling-3.1-flash, block FP8, pinned ata6943f4b; its real layers read one at a time.Serving (
ling-lanes, under upstream's round loop, prompts in 37-row chunks), bf16, FP8 and NVFP4, each with and without--mtp: 100 and 109 checks pass in each format. Tokens in every check, state bytes in the engine-level ones:Served logits against
ling_forward.py's float64 on the same picks: 2.09e-2 to 2.24e-2 rel L2, as far as the HF model's own bf16 rounding points are (2.10e-2 to 2.25e-2). On the real FP8 release,--layers 0,1,2,3(real embedding, layers 0-3, final norm and head) passes the same exactness checks; that run is not in the re-run at this head.Graph exactness, bit for bit, in bf16, FP8 and NVFP4 (
ling-forward-synth): a prompt cut at 48 equals the uncut prompt; an 8-row verify window equals 8 decode rows; keeping 3 of 8 rows and then running 5 more leaves every KDA state, conv window and MLA cache row equal to the serial run; two sequences batched equal each run alone. On real layers, a prompt cut at 300 equals the uncut prompt and a 16-row window equals 16 decode rows.Against the reference:
a78a7a49's;ling-mtp-real) against HF'sBailingMoeV3MTPLayer: 7.28e-3 on the 488 of 511 rows whose top-8 agrees;Full checkpoint (
ling-ckpt-check, shard headers only): 54 shards, 214,025 tensors (3,100 of the MTP layer), 573,753,281,152 bytes, no name, dtype or shape mismatch, nothing missing or extra. The shard data is not hashed.Fault paths (
ling-init-faults, onfake_driver.zig): every driver call and host allocation of the open and a kept state fails in turn with transfers pending, then every call from N on. Over bf16, FP8 and NVFP4, with and without MTP: 15,180 failure points (1,804 failing with a transfer pending), none leaves anything held, leaked, freed twice or freed before a drain.Loading one MoE layer's 512 FP8 experts (7.5 GiB): 6.1 s from local disk with the GPU packer, 14.3 s with the host packers before; host peak 1.3 GiB. Every packed byte equals the host packers' (
ling-pack).Decode and prompt speed: not measured. The whole model does not run on one Spark. The speed pass is measured per op only: prompt KDA and MLP column copies -50 % (
99652bab); FP8 expert time -7 to -27 % for prompt chunks up to 768 rows (5b006370).GLM unchanged:
glm-route-old(select, and swiglu at limits 10 and 0) andglm-mla-same(13 launches, 14.3 MB of outputs) run glm: GLM-5.3-Flash (NVIDIA's NVFP4 checkpoint) as a CUDA family at TP over hosts #804's6380c8dafatbins next to the new ones, byte for byte;ling-route-oldchecks the division-free branch against the older select's bytes;cuobjdump, all fatbins);ling-clamp-oldchecks the kept expert instances' bytes;c48be499,ac58df63ande29e0c20(now03171bf5): one host, TP=2 and TP=3, and the server at TP=2 against6380c8da's replies. All pass. GLM's and the shared code have not changed since03171bf5.Tests run at
1395e267:glm-ref,glm-route-old,ling-route-old,glm-mla-same,glm-kda-ling,ling-slots,ling-ckpt-check(three synthetic checkpoints and the full release),ling-init-faultsandling-lanes(three formats, with and without--mtp; ids and picks byte-equal toa78a7a49),ling-weights(three formats, worst 0.433 of the bound),ling-packlayer 4,ling-layer-reallayer 4;zig build test(1390/1398, 8 skipped),test-ling30/31 (1 skipped: it needsTF_LING_INDEX),test-cuda-host237/237,zig fmt --check,lean_check517, as on main. Deliberate mutations, all caught: routing 6, MLA 6, config 8, loader 5, graph 6, clamp 5, serving 5, MTP 5, and each review fix reverted alone.Not run, and why:
What is still open
glm/cuda_shard.zigand GLM-5.3-Flash on two CUDA ranks, in families/glm #855.--recordis refused forbailing_hybrid. The rows are the final-norm rowsEngine.verifyalready keeps.Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (On synthetic checkpoints in three formats and the real model's first 4 layers; not on the full model.)Open question for @ashhart (from #850 and your reply of 2026-10-15): group-limited top-k is built into #804's
tf_glm_topk, since Ling's MoE runs through #804's routing. It ranks sigmoid(logit)+bias as HF does for Ling; #798'smoe_glue.curanks logit+bias, so the two can't simply replace each other. Where should the shared routing live?