3030//!
3131//! Run: `cargo run --release --example gridlake_field_tile --features std`
3232
33- use ndarray:: hpc:: amx_matmul:: amx_available;
34- use ndarray:: hpc:: bf16_tile_gemm:: { bf16_tile_gemm_16x16, bf16_tile_gemm_tier} ;
35- use ndarray:: hpc:: quantized:: { f32_to_bf16_rounded, BF16 } ;
36-
37- const M : usize = 16 ; // tile edge — the AMX tile / gridlake quadrant
38- const KPAD : usize = 32 ; // bf16_tile_gemm requires K a multiple of 32; pad 16→32 with zeros
39-
40- fn mix ( mut z : u64 ) -> u64 {
41- z = z. wrapping_add ( 0x9E37_79B9_7F4A_7C15 ) ;
42- z = ( z ^ ( z >> 30 ) ) . wrapping_mul ( 0xBF58_476D_1CE4_E5B9 ) ;
43- z = ( z ^ ( z >> 27 ) ) . wrapping_mul ( 0x94D0_49BB_1331_11EB ) ;
44- z ^ ( z >> 31 )
45- }
46-
47- /// Pack a row-major `M×KPAD` (or `KPAD×M`) f32 matrix into the `&[u16]` bf16
48- /// operand the tile GEMM consumes (RNE, matching `VCVTNEPS2BF16`).
49- fn to_bf16 ( src : & [ f32 ] ) -> Vec < u16 > {
50- let mut b = vec ! [ BF16 ( 0 ) ; src. len( ) ] ;
51- f32_to_bf16_rounded ( src, & mut b) ;
52- b. iter ( ) . map ( |x| x. 0 ) . collect ( )
53- }
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+ }
5455
55- /// `C[16×16] = A[16×16] · B[16×16]` via the SHIPPED BF16 tile op. A/B are padded
56- /// on K from 16→32 with zeros (the pad contributes nothing), so this is one
57- /// `bf16_tile_gemm_16x16` — one `TDPBF16PS` tile op on the AMX tier.
58- fn tile_matmul ( a : & [ f32 ; M * M ] , b : & [ f32 ; M * M ] ) -> [ f32 ; M * M ] {
59- let mut a_pad = [ 0.0f32 ; M * KPAD ] ;
60- let mut b_pad = [ 0.0f32 ; KPAD * M ] ;
61- for i in 0 ..M {
62- for k in 0 ..M {
63- a_pad[ i * KPAD + k] = a[ i * M + k] ; // A[16×32], cols 16..31 = 0
64- b_pad[ k * M + i] = b[ k * M + i] ; // B[32×16], rows 16..31 = 0 (already)
65- }
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 ( )
6662 }
67- let ( ab, bb) = ( to_bf16 ( & a_pad) , to_bf16 ( & b_pad) ) ;
68- let mut c = vec ! [ 0.0f32 ; M * M ] ;
69- bf16_tile_gemm_16x16 ( & ab, & bb, & mut c, KPAD ) ;
70- let mut out = [ 0.0f32 ; M * M ] ;
71- out. copy_from_slice ( & c) ;
72- out
73- }
7463
75- /// Direct `f64` reference GEMM `C = A·B` (16×16).
76- fn direct_matmul ( a : & [ f32 ; M * M ] , b : & [ f32 ; M * M ] ) -> [ f64 ; M * M ] {
77- let mut c = [ 0.0f64 ; M * M ] ;
78- for i in 0 ..M {
79- for j in 0 ..M {
80- let mut s = 0.0f64 ;
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 {
8171 for k in 0 ..M {
82- s += a[ i * M + k] as f64 * b[ k * M + j] as f64 ;
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)
8374 }
84- c[ i * M + j] = s;
8575 }
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
8682 }
87- c
88- }
8983
90- fn transpose ( a : & [ f32 ; M * M ] ) -> [ f32 ; M * M ] {
91- let mut t = [ 0.0f32 ; M * M ] ;
92- for i in 0 ..M {
93- for j in 0 ..M {
94- t[ j * M + i] = a[ i * M + j] ;
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+ }
9595 }
96+ c
9697 }
97- t
98- }
9998
100- /// `(max_abs_err, frobenius_relative_err)` of a tile-GEMM result vs the f64
101- /// direct. Frobenius-relative `‖tile − direct‖_F / ‖direct‖_F` is the honest
102- /// aggregate precision — a per-element max-relative blows up on the near-zero
103- /// entries that a cancellation-heavy product (e.g. `Mᵀ·Σ·M`) naturally has,
104- /// which measures the cancellation, not the kernel.
105- fn errors ( tile : & [ f32 ; M * M ] , direct : & [ f64 ; M * M ] ) -> ( f64 , f64 ) {
106- let mut max_abs = 0.0f64 ;
107- let mut num = 0.0f64 ; // ‖E‖_F²
108- let mut den = 0.0f64 ; // ‖direct‖_F²
109- for i in 0 ..M * M {
110- let e = tile[ i] as f64 - direct[ i] ;
111- max_abs = max_abs. max ( e. abs ( ) ) ;
112- num += e * e;
113- den += direct[ i] * direct[ i] ;
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
114107 }
115- ( max_abs, ( num / den. max ( 1e-12 ) ) . sqrt ( ) )
116- }
117108
118- fn main ( ) {
119- println ! ( "Gridlake field interdependency = BF16 16×16 AMX tile op — measured" ) ;
120- println ! ( " tile M=16 (the AMX tile; 64×64 gridlake = 4×4 = 16 of these), K padded 16→32" ) ;
121- println ! (
122- " shipped kernel tier on THIS host: {} (amx_available = {}) \n " ,
123- bf16_tile_gemm_tier ( ) ,
124- amx_available ( )
125- ) ;
126-
127- // ── field tile X: a correlated gridlake patch (smooth → strong interdependency)
128- let mut x = [ 0.0f32 ; M * M ] ;
129- for r in 0 .. M {
130- for c in 0 .. M {
131- 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 ) ;
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 ] ;
132123 }
124+ ( max_abs, ( num / den. max ( 1e-12 ) ) . sqrt ( ) )
133125 }
134126
135- // ── (1) intercorrelation C = Xᵀ·X — the inter-cell coupling as a field op ──
136- let xt = transpose ( & x) ;
137- let corr_tile = tile_matmul ( & xt, & x) ;
138- let corr_direct = direct_matmul ( & xt, & x) ;
139- let ( ca, cr) = errors ( & corr_tile, & corr_direct) ;
140- println ! ( " (1) intercorrelation C = Xᵀ·X (one tile op)" ) ;
141- println ! ( " max_abs_err={ca:.4} frobenius_rel_err={:.3}% (BF16-precision class)" , cr * 100.0 ) ;
142-
143- // ── (2) covariance sandwich Σ' = Mᵀ·Σ·M — the splat EWA projection shape ──
144- // Σ = a symmetric PSD covariance (Xᵀ·X); M = a coupling/projection matrix.
145- let sigma_f: [ f32 ; M * M ] = {
146- let mut s = [ 0.0f32 ; M * M ] ;
147- for i in 0 ..M * M {
148- s[ i] = corr_direct[ i] as f32 ;
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+ }
149142 }
150- s
151- } ;
152- let mut m_proj = [ 0.0f32 ; M * M ] ;
153- for r in 0 ..M {
154- for c in 0 ..M {
155- m_proj[ r * M + c] = if r == c {
156- 1.0
157- } else {
158- 0.15 * ( r as f32 - c as f32 ) . signum ( )
159- } ;
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+ }
160170 }
161- }
162- // tile path: T = Σ·M, then Σ' = Mᵀ·T (two tile ops — the sandwich)
163- let t_tile = tile_matmul ( & sigma_f, & m_proj) ;
164- let mt = transpose ( & m_proj) ;
165- let sand_tile = tile_matmul ( & mt, & t_tile) ;
166- // direct reference: Mᵀ·Σ·M in f64
167- let t_direct = {
168- let mut td = [ 0.0f32 ; M * M ] ;
169- let d = direct_matmul ( & sigma_f, & m_proj) ;
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 ] ;
170196 for i in 0 ..M * M {
171- td[ i] = d[ i] as f32 ;
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 ;
172199 }
173- td
174- } ;
175- let sand_direct = direct_matmul ( & mt, & t_direct) ;
176- let ( sa, sr) = errors ( & sand_tile, & sand_direct) ;
177- println ! ( " (2) covariance sandwich Σ' = Mᵀ·Σ·M (two tile ops = the splat EWA projection)" ) ;
178- println ! (
179- " max_abs_err={sa:.4} frobenius_rel_err={:.3}% (== hpc::splat3d::sandwich_x16 shape;" ,
180- sr * 100.0
181- ) ;
182- println ! ( " per-entry max-rel is meaningless here — Mᵀ·Σ·M cancels to near-zero entries)" ) ;
183-
184- // ── (3) bit-exactness on bf16-exact integer operands (module guarantee) ──
185- let mut ai = [ 0.0f32 ; M * M ] ;
186- let mut bi = [ 0.0f32 ; M * M ] ;
187- for i in 0 ..M * M {
188- ai[ i] = ( ( mix ( i as u64 ) % 16 ) as f32 ) - 8.0 ; // small ints, bf16-exact
189- bi[ i] = ( ( mix ( i as u64 ^ 0x5555 ) % 16 ) as f32 ) - 8.0 ;
190- }
191- let int_tile = tile_matmul ( & ai, & bi) ;
192- let int_direct = direct_matmul ( & ai, & bi) ;
193- let bit_exact = ( 0 ..M * M ) . all ( |i| int_tile[ i] as f64 == int_direct[ i] ) ;
194- println ! ( " (3) bit-exact on bf16-exact integer operands (|Σ| < 2^24): {bit_exact}" ) ;
195-
196- // ── (4) throughput of the 16×16 tile op on this host ──
197- let iters = 200_000usize ;
198- let t0 = std:: time:: Instant :: now ( ) ;
199- let mut acc = 0.0f32 ;
200- for it in 0 ..iters {
201- // vary the operand slightly so nothing is optimized away
202- let mut xv = x;
203- xv[ 0 ] += ( it & 0x7 ) as f32 * 0.01 ;
204- let c = tile_matmul ( & xv, & m_proj) ;
205- acc += c[ 0 ] + c[ M * M - 1 ] ;
206- }
207- let dt = t0. elapsed ( ) . as_secs_f64 ( ) ;
208- let tiles_s = iters as f64 / dt;
209- println ! (
210- " (4) throughput: {iters} tile ops in {:.1} ms → {:.2} M tile-ops/s (checksum {acc:.1})" ,
211- dt * 1000.0 ,
212- tiles_s / 1e6
213- ) ;
214-
215- // hard gate: BF16-precision class (rel err small) + integer bit-exactness
216- assert ! ( cr < 0.05 , "intercorrelation rel err too high for BF16 class" ) ;
217- assert ! ( sr < 0.05 , "sandwich rel err too high for BF16 class" ) ;
218- assert ! ( bit_exact, "bf16-exact integer tile op is NOT bit-exact vs direct" ) ;
219-
220- println ! (
221- "\n MEASURED CONCLUSION: YES — a gridlake field interdependency reduces to a\n \
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 \
222231 \x20 BF16 16×16 tile op (one `bf16_tile_gemm_16x16`, i.e. one `TDPBF16PS` on\n \
223232 \x20 the AMX tier), matching the direct f64 reference to BF16 precision and\n \
224233 \x20 BIT-EXACT for bf16-exact integer operands. It IS the same primitive as:\n \
@@ -228,5 +237,16 @@ fn main() {
228237 \x20 Interdependency / intercorrelation / perturbation / transform / covariance\n \
229238 \x20 projection are ONE op: the 16×16 BF16 tile GEMM. The 64×64 gridlake is\n \
230239 \x20 4×4 = 16 of these tiles."
231- ) ;
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)." ) ;
232252}
0 commit comments