Skip to content

Commit 071d40a

Browse files
authored
Merge pull request #233 from AdaWorldAPI/claude/gridlake-field-tile
Probe: gridlake field interdependency = one BF16 16×16 AMX tile op (measured on real AMX)
2 parents 1ea77e8 + f10c2c4 commit 071d40a

3 files changed

Lines changed: 261 additions & 0 deletions

File tree

.gitattributes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Additive edits to the [[example]] list collide constantly across topic
2+
# branches. The built-in union merge driver keeps both sides of a conflict
3+
# hunk, auto-resolving append-only lists. (Distinct dependency-line edits are
4+
# caught by CI, not silently merged wrong.)
5+
Cargo.toml merge=union

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ required-features = ["codec"]
9595
name = "motion_transform_noop"
9696
required-features = ["codec"]
9797

98+
[[example]]
99+
name = "gridlake_field_tile"
100+
required-features = ["std"]
101+
98102
[[example]]
99103
name = "entropy_ladder_probe"
100104
required-features = ["std"]

examples/gridlake_field_tile.rs

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
//! Gridlake field interdependency = one BF16 16×16 AMX tile op — measured.
2+
//!
3+
//! The operator's claim (2026-07-04): a **gridlake**'s cell *interdependency*,
4+
//! treated as a *field operation*, reduces to a **BF16 16×16 AMX tile GEMM**
5+
//! (`TDPBF16PS`) — the same shape as the **intercorrelation / perturbation in a
6+
//! Gaussian splat** (the EWA covariance sandwich `Σ' = Mᵀ·Σ·M`, shipped as
7+
//! `hpc::splat3d::spd3::sandwich_x16`).
8+
//!
9+
//! Why it's true, mechanically: a *field operation* whose output cell is a linear
10+
//! combination of input cells is a linear operator `C = A·B` — a GEMM. The
11+
//! "interdependency" / "intercorrelation" IS the coupling matrix (the off-diagonal
12+
//! second-moment structure). At the gridlake tile granularity (16×16, one quarter
13+
//! of the 64×64 gridlake in each axis → the AMX tile) that GEMM is exactly one
14+
//! `hpc::bf16_tile_gemm::bf16_tile_gemm_16x16` call — one `TDPBF16PS` tile op.
15+
//! This is the SAME primitive as:
16+
//! • the codec separable transform (#232: `M·X`, the WHT/DCT on a tile), and
17+
//! • the splat EWA covariance projection (`sandwich_x16`, `Mᵀ·Σ·M`).
18+
//! One op, three names.
19+
//!
20+
//! This probe MEASURES the reduction on the shipped kernel (whatever tier the host
21+
//! selects — AMX `TDPBF16PS` / AVX-512 `VDPBF16PS` / F32x16 polyfill), reports the
22+
//! tier, and checks against a direct `f64` reference:
23+
//! 1. **intercorrelation** `C = Xᵀ·X` of a gridlake field tile (the inter-cell
24+
//! coupling as a field op) — one tile GEMM.
25+
//! 2. **covariance sandwich** `Σ' = Mᵀ·Σ·M` (the splat EWA projection shape) —
26+
//! two tile GEMMs.
27+
//! 3. **bit-exactness** on bf16-exact integer operands (the module's guarantee),
28+
//! and BF16-precision relative error on a continuous field.
29+
//! 4. **throughput** of the 16×16 tile op on this host.
30+
//!
31+
//! Run: `cargo run --release --example gridlake_field_tile --features std`
32+
33+
// The AMX / BF16-tile path is x86_64-only: `hpc::amx_matmul` and
34+
// `hpc::bf16_tile_gemm` are `#[cfg(target_arch = "x86_64")]`, and the canonical
35+
// `ndarray::simd::*` re-exports of the tile ladder are arch-gated to match. Wrap
36+
// the whole probe in an x86 module with a non-x86 fallback `main`, so a
37+
// cross-target `cargo check --examples` does not hit unresolved imports.
38+
#[cfg(target_arch = "x86_64")]
39+
mod amx {
40+
// Canonical public surface (`ndarray::simd::*`, per the W1a consumer contract),
41+
// not the raw `hpc::*` paths. `bf16_tile_gemm_16x16_amx` is the tile-dispatching
42+
// TDPBF16PS wrapper (aliased to the name the body already uses).
43+
use ndarray::hpc::quantized::{f32_to_bf16_rounded, BF16};
44+
use ndarray::simd::{amx_available, bf16_tile_gemm_16x16_amx as bf16_tile_gemm_16x16, bf16_tile_gemm_tier};
45+
46+
const M: usize = 16; // tile edge — the AMX tile / gridlake quadrant
47+
const KPAD: usize = 32; // bf16_tile_gemm requires K a multiple of 32; pad 16→32 with zeros
48+
49+
fn mix(mut z: u64) -> u64 {
50+
z = z.wrapping_add(0x9E37_79B9_7F4A_7C15);
51+
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
52+
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
53+
z ^ (z >> 31)
54+
}
55+
56+
/// Pack a row-major `M×KPAD` (or `KPAD×M`) f32 matrix into the `&[u16]` bf16
57+
/// operand the tile GEMM consumes (RNE, matching `VCVTNEPS2BF16`).
58+
fn to_bf16(src: &[f32]) -> Vec<u16> {
59+
let mut b = vec![BF16(0); src.len()];
60+
f32_to_bf16_rounded(src, &mut b);
61+
b.iter().map(|x| x.0).collect()
62+
}
63+
64+
/// `C[16×16] = A[16×16] · B[16×16]` via the SHIPPED BF16 tile op. A/B are padded
65+
/// on K from 16→32 with zeros (the pad contributes nothing), so this is one
66+
/// `bf16_tile_gemm_16x16` — one `TDPBF16PS` tile op on the AMX tier.
67+
fn tile_matmul(a: &[f32; M * M], b: &[f32; M * M]) -> [f32; M * M] {
68+
let mut a_pad = [0.0f32; M * KPAD];
69+
let mut b_pad = [0.0f32; KPAD * M];
70+
for i in 0..M {
71+
for k in 0..M {
72+
a_pad[i * KPAD + k] = a[i * M + k]; // A[16×32], cols 16..31 = 0
73+
b_pad[k * M + i] = b[k * M + i]; // B[32×16], rows 16..31 = 0 (already)
74+
}
75+
}
76+
let (ab, bb) = (to_bf16(&a_pad), to_bf16(&b_pad));
77+
let mut c = vec![0.0f32; M * M];
78+
bf16_tile_gemm_16x16(&ab, &bb, &mut c, KPAD);
79+
let mut out = [0.0f32; M * M];
80+
out.copy_from_slice(&c);
81+
out
82+
}
83+
84+
/// Direct `f64` reference GEMM `C = A·B` (16×16).
85+
fn direct_matmul(a: &[f32; M * M], b: &[f32; M * M]) -> [f64; M * M] {
86+
let mut c = [0.0f64; M * M];
87+
for i in 0..M {
88+
for j in 0..M {
89+
let mut s = 0.0f64;
90+
for k in 0..M {
91+
s += a[i * M + k] as f64 * b[k * M + j] as f64;
92+
}
93+
c[i * M + j] = s;
94+
}
95+
}
96+
c
97+
}
98+
99+
fn transpose(a: &[f32; M * M]) -> [f32; M * M] {
100+
let mut t = [0.0f32; M * M];
101+
for i in 0..M {
102+
for j in 0..M {
103+
t[j * M + i] = a[i * M + j];
104+
}
105+
}
106+
t
107+
}
108+
109+
/// `(max_abs_err, frobenius_relative_err)` of a tile-GEMM result vs the f64
110+
/// direct. Frobenius-relative `‖tile − direct‖_F / ‖direct‖_F` is the honest
111+
/// aggregate precision — a per-element max-relative blows up on the near-zero
112+
/// entries that a cancellation-heavy product (e.g. `Mᵀ·Σ·M`) naturally has,
113+
/// which measures the cancellation, not the kernel.
114+
fn errors(tile: &[f32; M * M], direct: &[f64; M * M]) -> (f64, f64) {
115+
let mut max_abs = 0.0f64;
116+
let mut num = 0.0f64; // ‖E‖_F²
117+
let mut den = 0.0f64; // ‖direct‖_F²
118+
for i in 0..M * M {
119+
let e = tile[i] as f64 - direct[i];
120+
max_abs = max_abs.max(e.abs());
121+
num += e * e;
122+
den += direct[i] * direct[i];
123+
}
124+
(max_abs, (num / den.max(1e-12)).sqrt())
125+
}
126+
127+
pub fn run() {
128+
println!("Gridlake field interdependency = BF16 16×16 AMX tile op — measured");
129+
println!(" tile M=16 (the AMX tile; 64×64 gridlake = 4×4 = 16 of these), K padded 16→32");
130+
println!(
131+
" shipped kernel tier on THIS host: {} (amx_available = {})\n",
132+
bf16_tile_gemm_tier(),
133+
amx_available()
134+
);
135+
136+
// ── field tile X: a correlated gridlake patch (smooth → strong interdependency)
137+
let mut x = [0.0f32; M * M];
138+
for r in 0..M {
139+
for c in 0..M {
140+
x[r * M + c] = 4.0 * (r as f32 * 0.4).sin() * (c as f32 * 0.35).cos() + 0.5 * (r as f32 - c as f32);
141+
}
142+
}
143+
144+
// ── (1) intercorrelation C = Xᵀ·X — the inter-cell coupling as a field op ──
145+
let xt = transpose(&x);
146+
let corr_tile = tile_matmul(&xt, &x);
147+
let corr_direct = direct_matmul(&xt, &x);
148+
let (ca, cr) = errors(&corr_tile, &corr_direct);
149+
println!(" (1) intercorrelation C = Xᵀ·X (one tile op)");
150+
println!(" max_abs_err={ca:.4} frobenius_rel_err={:.3}% (BF16-precision class)", cr * 100.0);
151+
152+
// ── (2) covariance sandwich Σ' = Mᵀ·Σ·M — the splat EWA projection shape ──
153+
// Σ = a symmetric PSD covariance (Xᵀ·X); M = a coupling/projection matrix.
154+
let sigma_f: [f32; M * M] = {
155+
let mut s = [0.0f32; M * M];
156+
for i in 0..M * M {
157+
s[i] = corr_direct[i] as f32;
158+
}
159+
s
160+
};
161+
let mut m_proj = [0.0f32; M * M];
162+
for r in 0..M {
163+
for c in 0..M {
164+
m_proj[r * M + c] = if r == c {
165+
1.0
166+
} else {
167+
0.15 * (r as f32 - c as f32).signum()
168+
};
169+
}
170+
}
171+
// tile path: T = Σ·M, then Σ' = Mᵀ·T (two tile ops — the sandwich)
172+
let t_tile = tile_matmul(&sigma_f, &m_proj);
173+
let mt = transpose(&m_proj);
174+
let sand_tile = tile_matmul(&mt, &t_tile);
175+
// direct reference: Mᵀ·Σ·M in f64
176+
let t_direct = {
177+
let mut td = [0.0f32; M * M];
178+
let d = direct_matmul(&sigma_f, &m_proj);
179+
for i in 0..M * M {
180+
td[i] = d[i] as f32;
181+
}
182+
td
183+
};
184+
let sand_direct = direct_matmul(&mt, &t_direct);
185+
let (sa, sr) = errors(&sand_tile, &sand_direct);
186+
println!(" (2) covariance sandwich Σ' = Mᵀ·Σ·M (two tile ops = the splat EWA projection)");
187+
println!(
188+
" max_abs_err={sa:.4} frobenius_rel_err={:.3}% (== hpc::splat3d::sandwich_x16 shape;",
189+
sr * 100.0
190+
);
191+
println!(" per-entry max-rel is meaningless here — Mᵀ·Σ·M cancels to near-zero entries)");
192+
193+
// ── (3) bit-exactness on bf16-exact integer operands (module guarantee) ──
194+
let mut ai = [0.0f32; M * M];
195+
let mut bi = [0.0f32; M * M];
196+
for i in 0..M * M {
197+
ai[i] = ((mix(i as u64) % 16) as f32) - 8.0; // small ints, bf16-exact
198+
bi[i] = ((mix(i as u64 ^ 0x5555) % 16) as f32) - 8.0;
199+
}
200+
let int_tile = tile_matmul(&ai, &bi);
201+
let int_direct = direct_matmul(&ai, &bi);
202+
let bit_exact = (0..M * M).all(|i| int_tile[i] as f64 == int_direct[i]);
203+
println!(" (3) bit-exact on bf16-exact integer operands (|Σ| < 2^24): {bit_exact}");
204+
205+
// ── (4) throughput of the 16×16 tile op on this host ──
206+
let iters = 200_000usize;
207+
let t0 = std::time::Instant::now();
208+
let mut acc = 0.0f32;
209+
for it in 0..iters {
210+
// vary the operand slightly so nothing is optimized away
211+
let mut xv = x;
212+
xv[0] += (it & 0x7) as f32 * 0.01;
213+
let c = tile_matmul(&xv, &m_proj);
214+
acc += c[0] + c[M * M - 1];
215+
}
216+
let dt = t0.elapsed().as_secs_f64();
217+
let tiles_s = iters as f64 / dt;
218+
println!(
219+
" (4) throughput: {iters} tile ops in {:.1} ms → {:.2} M tile-ops/s (checksum {acc:.1})",
220+
dt * 1000.0,
221+
tiles_s / 1e6
222+
);
223+
224+
// hard gate: BF16-precision class (rel err small) + integer bit-exactness
225+
assert!(cr < 0.05, "intercorrelation rel err too high for BF16 class");
226+
assert!(sr < 0.05, "sandwich rel err too high for BF16 class");
227+
assert!(bit_exact, "bf16-exact integer tile op is NOT bit-exact vs direct");
228+
229+
println!(
230+
"\n MEASURED CONCLUSION: YES — a gridlake field interdependency reduces to a\n\
231+
\x20 BF16 16×16 tile op (one `bf16_tile_gemm_16x16`, i.e. one `TDPBF16PS` on\n\
232+
\x20 the AMX tier), matching the direct f64 reference to BF16 precision and\n\
233+
\x20 BIT-EXACT for bf16-exact integer operands. It IS the same primitive as:\n\
234+
\x20 • the codec separable transform (#232: M·X on a tile — WHT/DCT), and\n\
235+
\x20 • the Gaussian-splat EWA covariance sandwich Σ'=Mᵀ·Σ·M\n\
236+
\x20 (hpc::splat3d::spd3::sandwich_x16 — the 16-wide batched form).\n\
237+
\x20 Interdependency / intercorrelation / perturbation / transform / covariance\n\
238+
\x20 projection are ONE op: the 16×16 BF16 tile GEMM. The 64×64 gridlake is\n\
239+
\x20 4×4 = 16 of these tiles."
240+
);
241+
}
242+
} // mod amx
243+
244+
#[cfg(target_arch = "x86_64")]
245+
fn main() {
246+
amx::run();
247+
}
248+
249+
#[cfg(not(target_arch = "x86_64"))]
250+
fn main() {
251+
eprintln!("gridlake_field_tile requires x86_64 (AMX TDPBF16PS / AVX-512 VDPBF16PS BF16 tile GEMM).");
252+
}

0 commit comments

Comments
 (0)