Adding a model family is not a marketing checkbox. It is a chain of conversion, template matching, tensor binding, RoPE validation, kernel selection, and measurable comparison against reference behavior. GLM4 is now represented in CKE's explicit compilation path, but each evidence level remains separate.
C-Kernel-Engine (CKE) is a Linux CPU runtime that turns explicit model and kernel contracts into generated C. Its v8 model matrix includes Qwen, Gemma, Nemotron, NaanBeige, and other model-family lanes at different maturity levels. GLM4 is one of those lanes, and this post follows what its bring-up required at the circuit, conversion, generated-dataflow, and runtime-smoke levels.
GLM4 is interesting because it is architecturally almost the same as Qwen/Llama-style models — dense decoder, RMSNorm, and a SWiGLU FFN — but has enough differences that a generic "llama-like" template can silently produce wrong output. For the GLM-4-9B-0414 lane, those differences include pairwise partial RoPE, QKV bias, post-attention and post-FFN norms, and the conditional [gMASK] prompting contract.
What CKE has actually established
The repository contains a GLM4 circuit, GGUF metadata handling, declarative safetensors mapping, synthetic conversion tests, generated-dataflow tests, PyTorch comparison tools, and a coherent Q4_K_M GGUF smoke path. The public architecture dashboard still marks runtime coverage as partial. This is not a claim of broad parity across every GLM checkpoint, quantization, ISA, compiler, prompt length, or thread count.
The implementation history is visible in PR #60, the initial GLM4 template and conversion lane, and PR #61, the dataflow and runtime hardening pass.
GLM4 vs The Field: What Actually Differs
| Feature | GLM4 | Qwen3 | Gemma 3 | Llama 3 |
|---|---|---|---|---|
| RoPE layout | Pairwise | Split-half | Split-half | Pairwise |
| Partial rotary | Yes (50%) | No (100%) | No (100%) | No (100%) |
| QKV bias | Yes | No | From weights | No |
| Post-attn norm | Yes | No | No | No |
| Post-FFN norm | Yes | No | No | No |
| Norm type | RMSNorm | RMSNorm | RMSNorm | RMSNorm |
| FFN | SWiGLU | SWiGLU | GeGLU | SWiGLU |
| Tokenizer contract | BPE | BPE | SentencePiece | BPE |
| QK norm | No | Yes | Yes | No |
| Prompt contract marker | [gMASK] when tokenizer BOS is disabled | <|im_start|> | Model-defined BOS | Model-defined BOS |
| Sizes (B) | 1B / 9B / 32B | 0.6B–235B | 1B–27B | 8B–405B |
Each bold cell is a contract CKE must represent rather than infer from a broad family resemblance. Pairwise RoPE rotates adjacent dimensions instead of CKE's split-half Qwen layout. Partial rotary leaves part of each head unrotated. Post-attention and post-FFN norms insert additional operations before their residual additions. A mismatch can remain numerically finite while still changing every downstream layer.
GLM4 transformer block: 13 IR operations in 12 visual stages
Click any stage to jump. The order follows CKE's glm4.json circuit; the SWiGLU stage groups mlp_gate_up and silu_mul so the diagram remains readable.
Op 1 of 12
RMSNorm (pre-attention)
Standard pre-attention RMSNorm. Normalizes the residual stream before Q/K/V projections. Same as Qwen3/Llama3 — nothing GLM4-specific here.
// ln1_gamma: [embed_dim] y = x * rsqrt(mean(x²) + eps) y = y * gamma
The CKE Bring-Up Pipeline
Every new family goes through the same categories of evidence, but they are not automatically all green. For GLM4, the circuit and conversion contracts are executable tests; the hidden-state and logit comparators are diagnostic tools; and the current runtime evidence is a coherent Q4_K_M smoke. Applying split-half RoPE to this pairwise checkpoint is one example of a defect that can preserve finite numbers while corrupting the model's semantics.
| Gate | What CKE checks | Tool | Failure tells you |
|---|---|---|---|
| Config audit | Architecture fields match circuit JSON | test_v8_glm4_template.py | Template contract mismatch |
| Tensor map | HF tensor names → CK names, no unmapped | safetensors_ck_map.json | Conversion bug or new tensor |
| Template body | Op sequence matches expected order | Circuit test assertions | Wrong block structure |
| Hidden comparison | Configured defaults: cosine > 0.995 and max absolute error < 2.0 | compare_glm4_torch_ck_hidden_v8.py | First failing exported boundary |
| Logit comparison | Configured defaults: top-k overlap ≥ 0.50 and top-1 match | compare_glm4_torch_ck_v8.py | Cumulative drift or final-head disagreement |
| Coherence | Generated text is natural language | cks-v8-run | Tokenizer/chat contract wrong |
What changed in the repository
| Change | CKE action | Evidence added | Boundary |
|---|---|---|---|
| PR #60 | Added glm4.json, GGUF metadata handling, safetensors aliases and mappings, synthetic conversion coverage, and a PyTorch logit comparator. | Template and conversion contracts became executable. | The original change explicitly remained a conversion/parity lane pending a high-memory runtime run. |
| PR #61 | Hardened pairwise partial RoPE, chat formatting, graph-slot wiring, quantized activation views, and hidden-state diagnostics. | Generated BF16 projections were checked against the normalized semantic stream; quantized projections were checked against its Q8 physical view. | A coherent Q4_K_M smoke is useful runtime evidence, not universal numerical parity. |
The second change is especially important. A circuit describes the semantic producer, such as normalized main_stream. A selected quantized kernel may physically consume main_stream_q8, a derived packed view. Encoding only one side either loses model meaning or lies about the ABI. The GLM4 tests now guard both views instead of treating them as interchangeable.
Click a stage to see its command, default threshold, and diagnostic purpose. A tool being present does not mean every real checkpoint has passed it.
GATE 01
Convert
implemented
▶
GATE 02
Template Audit
tested
▶
GATE 03
IR → Compile
smoke path
▶
GATE 04
Hidden Compare
harness
▶
GATE 05
Logit Compare
harness
▶
GATE 06
Coherence
Q4 smoke
Gate 01: Weight conversion — safetensors/GGUF → BUMP
version/v8/scripts/convert_safetensors_to_bump_v8.py \ --checkpoint ./glm4-9b-hf \ --arch glm4 --dtype bf16 \ --output weights.bump \ --config-out config.json --manifest-out manifest.json
What it does: Reads HF safetensors, validates every tensor against the glm4 template, repacks into 64-byte-aligned BUMP with canary words between regions.
Gate: no unmapped tensors (unless in the ignore list). Use --dry-run to validate mapping without writing weights.
Failure tells you: A new/renamed tensor the map does not know about — usually the GLM4-specific post_attention / post_ffn norms.
Reproduce This Yourself
The whole point of bring-up infrastructure is that it is runnable, not aspirational. Start with the low-memory contract tests. On a host with enough RAM for the model, use the documented GGUF smoke command. The comparison tools require a matching safetensors checkpoint and a generated CKE model directory; they are not automatically executed by the smoke command.
# GLM4 circuit/dataflow contract: no 9B checkpoint required .venv/bin/python -m unittest tests.test_v8_glm4_template # Optional conversion coverage requires the test dependencies .venv/bin/python -m pytest tests/test_v8_safetensors_to_bump.py -q # Current documented Q4_K_M runtime smoke version/v8/scripts/cks-v8-run run \ hf://unsloth/GLM-4-9B-0414-GGUF/GLM-4-9B-0414-Q4_K_M.gguf \ --context-len 1024 --force-compile --force-convert \ --chat-template=glm4 \ --prompt 'Give me a detailed example of C, Python and SQL code.' \ --max-tokens 256 --temperature 0.0 --generate-visualizer # Optional diagnostic: compare exported hidden boundaries version/v8/scripts/compare_glm4_torch_ck_hidden_v8.py \ --safetensors ./GLM-4-9B-0414 \ --ck-hidden-dir ./ck_hidden_dump \ --layers all --fail-cos 0.995 --fail-max 2.0 # Optional diagnostic: compare final logits version/v8/scripts/compare_glm4_torch_ck_v8.py \ --safetensors ./GLM-4-9B-0414 \ --ck-model-dir ./generated_glm4_model \ --top-k 16 --min-topk-overlap 0.50
The Tensor Map: HF → CKE
GLM4 uses HuggingFace naming conventions for its safetensors. CKE uses its own flat naming. The declarative mapping (safetensors_ck_map.json) handles this translation, including synthesized zero biases where HF has none but CKE's template contract expects them:
// safetensors → CKE tensor mapping (GLM4) model.embed_tokens.weight → token_emb model.layers.N.input_layernorm → layer.N.ln1_gamma model.layers.N.self_attn.q_proj → layer.N.wq model.layers.N.self_attn.q_proj.bias → layer.N.bq model.layers.N.self_attn.k_proj → layer.N.wk model.layers.N.self_attn.k_proj.bias → layer.N.bk model.layers.N.self_attn.v_proj → layer.N.wv model.layers.N.self_attn.v_proj.bias → layer.N.bv model.layers.N.self_attn.o_proj → layer.N.wo model.layers.N.post_attention_layernorm → layer.N.post_attention_norm ★ model.layers.N.pre_feedforward_layernorm → layer.N.ln2_gamma model.layers.N.mlp.gate_proj + up_proj → layer.N.w1 (concat) model.layers.N.mlp.down_proj → layer.N.w2 model.layers.N.post_feedforward_layernorm → layer.N.post_ffn_norm ★ model.norm.weight → final_ln_weight
The ★ tensors are GLM4-specific. If the mapper does not handle them, CKE would compile and run — but with wrong residual scaling at every layer.
Pairwise RoPE: Why It Matters
CKE's Qwen3 RoPE path uses a split-half layout: dimension i is paired with i + d/2. GLM4 uses a pairwise layout: adjacent pairs (0,1), (2,3), (4,5), ... are rotated together. Both are valid rotary layouts, but they are not interchangeable.
Additionally, GLM4 applies RoPE to only 50% of head dimensions (partial_rotary_factor = 0.5). The first half gets position information; the second half is position-agnostic. This is declared in the circuit JSON as partial_rotary: true and the converter reads partial_rotary_factor from the model config.
// Split-half RoPE (Qwen3):
// Pair dim[i] with dim[i + d/2]
for i in 0..d/2:
(q[i], q[i+d/2]) = rotate(q[i], q[i+d/2], theta*pos)
// Pairwise RoPE (GLM4):
// Pair dim[2i] with dim[2i+1]
for i in 0..d/2:
(q[2i], q[2i+1]) = rotate(q[2i], q[2i+1], theta*pos)
// Partial rotary (GLM4):
rotary_dim = head_dim // 2
q[:rotary_dim] = pairwise_rope(q[:rotary_dim], pos)
q[rotary_dim:] = q[rotary_dim:] // no rotation The Hidden-State Comparator
CKE's compare_glm4_torch_ck_hidden_v8.py hooks into every layer's intermediate tensors in the HuggingFace model and compares them against CKE's exported hidden states. This is how you find the first wrong operation:
| Hook point | CKE export file | Pass threshold |
|---|---|---|
| q_proj output | tok_0000_layer_000_q_proj.f32 | cos > 0.995, max_abs < 2.0 |
| k_proj output | tok_0000_layer_000_k_proj.f32 | cos > 0.995, max_abs < 2.0 |
| v_proj output | tok_0000_layer_000_v_proj.f32 | cos > 0.995, max_abs < 2.0 |
| o_proj output | tok_0000_layer_000_o_proj.f32 | cos > 0.995, max_abs < 2.0 |
| post_attn_norm | tok_0000_layer_000_post_attn_norm.f32 | cos > 0.995, max_abs < 2.0 |
| mlp_gate_up | tok_0000_layer_000_mlp_gate_up.f32 | cos > 0.995, max_abs < 2.0 |
| mlp_down | tok_0000_layer_000_mlp_down.f32 | cos > 0.995, max_abs < 2.0 |
| post_ffn_norm | tok_0000_layer_000_post_ffn_norm.f32 | cos > 0.995, max_abs < 2.0 |
| layer output | tok_0000_layer_000_layer_out.f32 | cos > 0.995, max_abs < 2.0 |
If q_proj agrees but post_attn_norm does not, the first exported failure lies after the Q projection and at or before that later boundary. The comparator narrows the search interval; it does not by itself prove which kernel inside that interval is wrong.
The GLM4 Chat Contract
GLM4 has a distinctive prompting contract. When the tokenizer's add_bos_token is false (common in HuggingFace configs), CKE forces the prefix [gMASK]. The chat template uses role markers like <|user|>\n and <|assistant|>\n. Stop markers include <|user|> and <|observation|>.
// GLM4 chat format [gMASK]<|system|> You are a helpful assistant. <|user|> What is pairwise RoPE? <|assistant|> {generated response} // Stop markers: <|user|>, <|observation|> // Min response tokens: 8
Zooming Out: GLM4 In The Full CKE Model Family
Everything above was GLM4 in isolation — its block, its RoPE, its parity gates. But the real payoff of the circuit-first approach only becomes visible when you step back and look at GLM4 next to every other family CKE supports. GLM4 bring-up validates the claim that the template/circuit system is genuinely family-agnostic: the same pipeline — circuit JSON → build_ir → codegen → compile → parity — handles GLM4's quirks (pairwise RoPE, partial rotary, extra norms, bias tensors) without any special-case code in the engine. The differences are declarative. They live in glm4.json and the tensor map, not in hand-written kernel branches.
The rest of this post is the wide-angle view: how GLM4's op sequence compares to Qwen, Gemma, Llama, and the more exotic hybrid and latent-attention architectures — and exactly which kernel decisions separate them. The three interactive diagrams below are built directly from the circuit JSON files in v8/circuits/.
CKE Model Architecture Comparison: Op Sequences Side By Side
Every model in CKE is defined by a circuit JSON file that declares its exact op sequence. This is not metadata — it is the source of truth the compiler uses to generate C code. Here is what each family's decoder block actually looks like, read directly from the v8/circuits/ directory:
kernel: rope_forward_qk_pairwise · Unique: post_attention_norm, post_ffn_norm, pairwise RoPE, partial rotary (50%), QKV biasKernel Divergence: What Breaks If You Use The Wrong One
The kernel is where the contract becomes computation. Each model family requires specific kernel variants — use the wrong one and output degrades silently. Here is the divergence map across CKE's supported families:
RoPE Layouts: Why The Same Math Produces Wrong Results
Rotary Position Embedding (RoPE) is the most dangerous divergence point — it affects every token at every layer, but the difference is invisible without parity testing. Three models, three layouts:
// Pairwise: rotate adjacent pairs
for (int i = 0; i < head_dim; i += 2) {
float x0 = q[i], x1 = q[i+1];
float cos_i = cos_cache[i/2];
float sin_i = sin_cache[i/2];
q[i] = x0 * cos_i - x1 * sin_i;
q[i+1] = x0 * sin_i + x1 * cos_i;
} Pairwise RoPE pairs physically adjacent dimensions: (0,1), (2,3), (4,5)… This is the original GPT-NeoX layout. GLM4 uses this but only on the first 50% of head_dim (partial_rotary_factor = 0.5). Llama uses it on 100%.
If you apply split-half RoPE to a pairwise model, the frequencies rotate the wrong dimension pairs. The result can remain finite and superficially plausible while disagreeing with the checkpoint contract.
Models using this layout: GLM4 (partial 50%), Llama 3, Nanbeige
Training Context: What Matters To The Runtime
The ChatGLM technical report explains the broader GLM family and its tool-oriented post-training, while the official GLM-4 repository records released checkpoints and usage conventions. Those sources are useful context, but CKE cannot infer an executable runtime contract from a training narrative alone.
CKE acts on checkpoint-visible facts: tensor names and shapes, tokenizer metadata, special-token policy, rotary dimensions, normalization tensors, attention geometry, and the declared chat format. Claims about why a model designer chose partial RoPE or post-block normalization are separate from proving how the released weights must execute.
The evidence boundary
The current GLM4 circuit is for checkpoints that match this exact contract. A newer GLM, GLM-Z, or multimodal release must be audited as a new checkpoint family. Similar branding does not prove identical tensor layouts, tokenizer behavior, dimensions, or kernel requirements.
Full CKE Model-Kernel Matrix
This table extends the official CKE model-kernel-matrix with architectural detail relevant to bring-up decisions. Every cell represents a contract decision that the template compiler must get right:
| Model | Body Ops | RoPE | Activation | Attention | QK-Norm | Post-Norms | Block Type | Quant Lanes |
|---|---|---|---|---|---|---|---|---|
| GLM4 | 13 | Pairwise (partial 50%) | SWiGLU | Dense GQA | No | Yes | Dense | GGUF Q4_K, Q8_0, BF16 safetensors |
| Qwen2 | 11 | Split-half | SWiGLU | Dense GQA | No | No | Dense | GGUF Q4_K_M, Q8_0 |
| Qwen3 | 12 | Split-half | SWiGLU | Dense GQA | Yes | No | Dense | GGUF Q4_K_M, Q8_0 |
| Qwen3.5 | 21/16 | M-RoPE (text) | SWiGLU | Hybrid recurrent+attn | Full-attn only | Yes (post_attn) | 3×recurrent → 1×attention | GGUF, BF16 |
| Gemma 3 | 14 | Split-half | GeGLU | Sliding window | Yes | Yes | Dense (sliding) | GGUF Q4_K_M, Q8_0 |
| Gemma 4 | 18 | Split-direct (per-layer θ) | GeGLU | Hybrid sliding/full | Yes | Yes | 4 layer kinds | GGUF Q4_K_M, Q6_K, BF16 |
| Llama | 11 | Pairwise | SWiGLU | Dense GQA | No | No | Dense | GGUF Q4_K_M |
| Nemotron-H | 9/7/5/6 | Split-half | ReLU² | Sparse + Mamba2 | No | No | 4 kinds (mamba/attn/mlp/moe) | GGUF Q4_K_M, Q5_0, Q8_0, BF16 |
| Kimi/MLA | 14+ | Partial pairwise concat | SWiGLU | MLA (latent compress) | No | No | MLA dense + MoE | BF16, FP32 reference |
| GPT-2 | — | Learned PosEmb | GELU | Dense MHA | No | No | Dense (LayerNorm) | GGUF baseline |
The pattern is clear: modern model diversity is not cosmetic. Each family makes different engineering decisions about how to encode position, gate information, mix sequences, and compress state. CKE handles this by making every decision explicit in the circuit JSON — the compiler reads the contract and selects the right kernel. No heuristics, no "llama-like" fallbacks.
Practical Takeaway
Model support is not a checkbox. It is a measurable pipeline: convert → template match → IR lower → compile → hidden parity → logit parity → coherence. GLM4 adds three new contract requirements to CKE's vocabulary (pairwise RoPE, partial rotary, post-block norms) — and each one would have caused silent degradation if the parity infrastructure did not catch it.
The kernel divergence map above shows why this matters at scale: as CKE grows to support 10+ model families, each with different RoPE layouts, activation functions, attention mechanisms, and state management contracts, the circuit-first approach prevents the combinatorial explosion that plagues "if-else" runtimes. The template is the contract. The contract is the compiler input. The compiler produces the right kernel calls or refuses to compile.
To continue from the model-family contract into code generation, read Templates Are Circuit Maps, then CKE's Kernel Registry. Together they explain how a circuit such as glm4.json becomes typed kernel calls rather than a pile of model-name conditionals.
- What Is the C-Kernel-Engine?
- v8 IR Pipeline Codegen: How CKE Turns Model Graphs Into C
- Templates Are Circuit Maps
- CKE Kernel Registry And Numerical Contracts
- Prefill vs Decode In CKE
- C-Kernel-Engine v8 Runbook
- CKE Model-Kernel Matrix (official)
- CKE Quantization Deep Dive
- K-Quants Deep Dive
- ChatGLM Technical Report (arXiv:2406.12793)
- GLM-4 GitHub Repository
- CKE GLM4 Circuit Source
- CKE GLM4 Contract Tests