File size: 74,714 Bytes
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
8058e44
 
 
 
 
 
 
 
 
58ba6df
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
 
 
 
 
 
 
 
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
8058e44
58ba6df
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
 
8058e44
 
 
 
 
 
 
 
58ba6df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
8058e44
58ba6df
 
 
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
 
8058e44
 
58ba6df
 
8058e44
 
 
 
 
58ba6df
 
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
58ba6df
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8058e44
 
58ba6df
8058e44
 
 
 
 
 
 
 
 
 
 
58ba6df
 
 
 
 
 
 
 
 
 
 
8058e44
 
 
58ba6df
 
 
 
 
8058e44
 
 
 
 
58ba6df
8058e44
 
 
 
 
 
 
 
 
 
 
 
58ba6df
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
58ba6df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
58ba6df
8058e44
 
 
 
 
 
 
 
 
58ba6df
8058e44
 
58ba6df
 
 
 
 
 
 
 
 
 
 
 
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
8058e44
 
58ba6df
 
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
8058e44
 
 
 
 
 
 
 
 
58ba6df
 
 
8058e44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ba6df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
# SPDX-FileCopyrightText: © 2026 Tenstorrent Inc.
# SPDX-License-Identifier: Apache-2.0
"""Host-only tests of the ``TT_FUSED=1`` path (torch only -- NO device, NO ttnn tensors).

What is proven here (BRIEF deliverable 3):

* every exact reformulation of ``hamer.fused`` against the reference math
  (``torch.equal`` / fp64 max|d|, or <= 1 bf16 ULP with the reason);
* the torch emulation of the whole fused ViT block and of the whole fused MANO
  head (padded heads, ``[Q|K|V]`` head-split layout, V-only self-attention,
  zero-Q fake K/V weight, constant ``x0``, fused regressor) equals the reference
  modules in fp64;
* knob plumbing: ``TT_FUSED=0`` -> legacy path selected, unset/1 -> fused (default), sub-knobs parsed;
* the fused DEVICE code (``vit._forward_fused`` / ``mano_head._forward_fused``)
  walked with a mock ``ttnn`` that enforces the validate rules read from this
  tree (rank/S%32/width rules of the head ops, the fused matmul's bias and
  ternary shape + dtype + buffer-type rules, SDPA without a mask, fp32
  ROW_MAJOR tilize input) and detects use-after-deallocate; launch counts
  match the device-validated graph (10 per ViT block, 16 per head layer, 420 per forward);
* ``model.py``'s per-request plumbing on the same mock (device injected, never
  opened): ``_host_input`` dtype/layout per knob, ``_upload_tokens`` persistent
  buffer + content cache + ``TT_FUSED_HASH=0`` bypass + shape-change re-allocation,
  and the batch-1 guard of the fused path raising even in non-strict mode.

Run (host python of the tt-metal tree; the real ttnn is imported by ``hamer.model``
but never asked for a tensor)::

    cd models/hamer-p150 && PYTHONPATH=code TT_METAL_HOME=<tree> HAMER_USE_TT=0 \
        <tree>/python_env/bin/python -m pytest code/tests/test_fused_host.py -q
"""
from __future__ import annotations

import math
import os
import sys
import types
from pathlib import Path

# The wrapper must never try to open a chip from a host test.
os.environ.setdefault("HAMER_USE_TT", "0")
os.environ.pop("TT_FUSED", None)

import pytest  # noqa: E402
import numpy as np
import torch  # noqa: E402
import torch.nn.functional as F  # noqa: E402

_REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(_REPO_ROOT))

from hamer import fused as fz  # noqa: E402
from hamer import mano_head as mh  # noqa: E402
from hamer import vit  # noqa: E402
from hamer import tt_kernels as tk  # noqa: E402
from hamer.fused import FusedOptions  # noqa: E402
from hamer.reference import Hamer, HamerConfig, sample_input  # noqa: E402

torch.manual_seed(0)
torch.set_grad_enabled(False)

# Small-depth reference with the REAL widths (1280 / 16 heads x 80 / 5120; head 1024 / 8 x 64):
# every shape, padding and layout below is the production one, only fewer blocks.
DEPTH, HEAD_DEPTH = 2, 2


@pytest.fixture(scope="module")
def ref() -> Hamer:
    torch.manual_seed(0)
    m = Hamer(HamerConfig(depth=DEPTH, head_depth=HEAD_DEPTH))
    # Random LN affine / pos so that nothing is trivially identity or zero.
    for n, p in m.named_parameters():
        if n.endswith(("norm1.weight", "norm2.weight", "last_norm.weight", "norm_sa.weight", "norm_ca.weight",
                       "norm_ff.weight")):
            p.copy_(1.0 + 0.1 * torch.randn_like(p))
        elif n.endswith(("norm1.bias", "norm2.bias", "last_norm.bias", "norm_sa.bias", "norm_ca.bias", "norm_ff.bias")):
            p.copy_(0.1 * torch.randn_like(p))
    m.backbone.pos_embed.copy_(0.02 * torch.randn_like(m.backbone.pos_embed))
    # the reference init sets the MANO mean buffers to zero; give them values so host_finalize is exercised
    m.head.init_hand_pose.copy_(0.1 * torch.randn_like(m.head.init_hand_pose))
    m.head.init_betas.copy_(0.1 * torch.randn_like(m.head.init_betas))
    m.head.init_cam.copy_(torch.tensor([[5.0, 0.01, -0.02]]))
    return m.eval()


@pytest.fixture(scope="module")
def image() -> torch.Tensor:
    return sample_input(batch=1, seed=3)


def _maxd(a: torch.Tensor, b: torch.Tensor) -> float:
    return float((a.double() - b.double()).abs().max())


# =========================================================================== #
# 1. Knob plumbing
# =========================================================================== #
def test_knob_default_on_and_parsing():
    # default ON since the p150a pass; TT_FUSED=0 is the legacy path
    assert FusedOptions.from_env({}).enabled is True
    assert FusedOptions.from_env({}) == FusedOptions.from_env({"TT_FUSED": "1"})
    assert FusedOptions.from_env({"TT_FUSED": "0"}) == FusedOptions()
    assert FusedOptions.from_env({"TT_FUSED": "0"}).enabled is False
    # a stray sub-knob never affects the legacy path (not even parsed)
    assert FusedOptions.from_env({"TT_FUSED": "0", "TT_FUSED_MM": "garbage"}) == FusedOptions()
    on = FusedOptions.from_env({"TT_FUSED": "1"})
    assert on.enabled and on.matmul == "tuned" and on.fc1 == "bfp8" and on.fc2 == "default"
    # tuned (chip-25 pass): qkv / fc2 / proj bfp8 with fp32 accumulation (proj bf16 without it);
    # minimal keeps the bf16 fusable set
    assert on.wdtype("fc2") == "bfp8" and on.wdtype("qkv_w") == "bfp8" and on.wdtype("proj_w") == "bfp8"
    noacc = FusedOptions.from_env({"TT_FUSED": "1", "TT_FUSED_FP32ACC": "0"})
    assert noacc.fp32_acc is False and noacc.wdtype("proj_w") == "bf16" and noacc.wdtype("fc2") == "bfp8"
    mini = FusedOptions.from_env({"TT_FUSED": "1", "TT_FUSED_MM": "minimal"})
    assert mini.wdtype("fc2") == mini.wdtype("qkv_w") == mini.wdtype("proj_w") == "bf16"
    # defaults: fp32 patch weight (p150a pass), 32x192 SDPA chunking (chip-25 pass)
    assert on.patch == "device" and on.device_im2col and on.sdpa == "32x192" and on.fidelity == "HiFi2" and on.head_concat == "nlp"
    assert on.head == "hg" and on.heads == "custom" and on.gelu == "kernel" and on.vit_shard is True
    assert on.l1 is True and on.head_l1 is False and on.fp32_acc is True and on.hash_cache is True
    assert on.device_patch_embed is True
    custom = FusedOptions.from_env({
        "TT_FUSED": "true", "TT_FUSED_MM": "linear", "TT_FUSED_FC1": "bf16", "TT_FUSED_FC2": "bfp8",
        "TT_FUSED_L1": "0", "TT_FUSED_PATCH": "host", "TT_FUSED_SDPA": "default", "TT_FUSED_FIDELITY": "hifi4",
        "TT_FUSED_FP32ACC": "1", "TT_FUSED_HEAD_CONCAT": "permute", "TT_FUSED_HASH": "off",
    })
    assert custom == FusedOptions(enabled=True, matmul="linear", fc1="bf16", fc2="bfp8", l1=False, patch="host",
                                  sdpa="default", fidelity="HiFi4", fp32_acc=True, head_concat="permute",
                                  hash_cache=False)
    assert custom.device_patch_embed is False
    with pytest.raises(ValueError):
        FusedOptions.from_env({"TT_FUSED": "1", "TT_FUSED_MM": "fast"})
    with pytest.raises(ValueError):
        FusedOptions.from_env({"TT_FUSED": "1", "TT_FUSED_L1": "maybe"})
    assert "TT_FUSED=0" in FusedOptions().describe() and "matmul=tuned" in on.describe()


def test_wrapper_reads_knob_at_build_not_import(ref, monkeypatch):
    """``TtHamer`` (HAMER_USE_TT=0 -> no device) picks the legacy path with ``TT_FUSED=0``,
    the fused one without the env (default) or with ``TT_FUSED=1``, and the explicit
    ``fused=`` argument wins over the env."""
    from hamer.model import TtHamer, build_from_reference

    monkeypatch.setenv("TT_FUSED", "0")
    m = TtHamer(ref)
    assert m.fused is False and m.fused_opts == FusedOptions() and m.device is None and m._vit_params is None
    monkeypatch.delenv("TT_FUSED", raising=False)
    assert TtHamer(ref).fused is True  # default ON
    monkeypatch.setenv("TT_FUSED", "1")
    monkeypatch.setenv("TT_FUSED_PATCH", "host")
    m2 = build_from_reference(ref)
    assert m2.fused is True and m2.fused_opts.patch == "host" and m2.fused_opts.device_patch_embed is False
    m3 = build_from_reference(ref, fused=FusedOptions())
    assert m3.fused is False  # explicit legacy object wins over the env
    monkeypatch.setenv("TT_FUSED", "0")
    assert TtHamer(ref).fused is False
    # legacy public surfaces are intact
    assert vit.forward.__defaults__[-1] is False and mh.forward_device.__defaults__[-1] is False
    assert vit.build_parameters_from_reference.__defaults__ == (None,)
    assert mh.build_parameters_from_reference.__defaults__ == (None,)


# =========================================================================== #
# 2. Patch embedding: exact im2col + constant table
# =========================================================================== #
def test_im2col_ordering_is_c_kh_kw(image):
    """Column j of the im2col = padded pixel (c, gh*16 + kh, gw*16 + kw) with j = c*256 + kh*16 + kw,
    the same (c, kh, kw) row order ``patch_embed_weight`` flattens the conv kernel to."""
    cols = fz.host_im2col(image)  # [1, 1, 192, 768]
    assert tuple(cols.shape) == (1, 1, fz.N_TOKENS, fz.PATCH_DIM) and cols.dtype == torch.float32
    padded = F.pad(image, (fz.PATCH_PAD,) * 4)  # [1, 3, 260, 196]; the stride-16 conv covers rows/cols < 256/192
    padded = padded[:, :, : fz.GRID_H * fz.PATCH, : fz.GRID_W * fz.PATCH]
    manual = padded.reshape(1, 3, fz.GRID_H, fz.PATCH, fz.GRID_W, fz.PATCH)          # c gh kh gw kw
    manual = manual.permute(0, 2, 4, 1, 3, 5).reshape(1, 1, fz.N_TOKENS, fz.PATCH_DIM)  # (gh gw) (c kh kw)
    assert torch.equal(cols, manual)


def test_im2col_matmul_equals_conv2d(ref, image):
    conv = ref.backbone.patch_embed.proj
    w = fz.patch_embed_weight(conv)                       # [768, 1280]
    assert torch.equal(w, vit._patch_embed_weight(conv))  # same flattening as the legacy helper
    tokens_ref, _ = ref.backbone.patch_embed(image)       # [1, 192, 1280] fp32 conv
    tokens = (fz.host_im2col(image)[0] @ w + conv.bias)   # [1, 192, 1280]
    d = _maxd(tokens, tokens_ref)
    assert d <= 1e-5, d                                   # fp32 accumulation order only
    # exact in fp64
    d64 = _maxd(fz.host_im2col(image).double()[0] @ w.double() + conv.bias.double(),
                F.conv2d(image.double(), conv.weight.double(), conv.bias.double(), stride=16, padding=2)
                .flatten(2).transpose(1, 2))
    assert d64 <= 1e-9, d64


def test_pos_plus_bias_constant(ref, image):
    """The fused patch op computes ``pos_plus_bias + im2col @ W``; that must equal the
    reference ``patch_embed(x) + pos[:, 1:] + pos[:, :1]`` (what the legacy path had after
    its device pos add)."""
    w, ppb = fz.patch_constants(ref.backbone)
    assert tuple(ppb.shape) == (1, 1, fz.N_TOKENS, fz.EMBED)
    pos = ref.backbone.pos_embed
    assert torch.equal(fz.pos_combined(ref.backbone), (pos[0, 1:] + pos[0, :1]))
    fused_tokens = fz.host_im2col(image)[0] @ w + ppb[0]
    d = _maxd(fused_tokens, fz.patch_tokens_reference(ref.backbone, image)[0])
    assert d <= 1e-5, d


# =========================================================================== #
# 3. ViT block: padding + head-split layout + fused graph == reference (fp64)
# =========================================================================== #
def _emulate_create_qkv_heads(qkv: torch.Tensor, num_heads: int):
    """Torch model of ``nlp_create_qkv_heads(transpose_k_heads=False)``: the reader kernel
    consumes each tile-row as [Q tiles | K tiles | V tiles] (q_num_tiles = H*dh/32 first),
    per head contiguous -> ``view(B, S, 3, H, dh)``."""
    B, one, S, W = qkv.shape
    assert one == 1 and S % 32 == 0 and W % (3 * num_heads) == 0
    dh = W // (3 * num_heads)
    t = qkv.reshape(B, S, 3, num_heads, dh).permute(2, 0, 3, 1, 4)
    return t[0], t[1], t[2]  # [B, H, S, dh]


def _emulate_concat_heads(x: torch.Tensor):
    B, H, S, dh = x.shape
    return x.permute(0, 2, 1, 3).reshape(B, 1, S, H * dh)


def test_qkv_padding_layout_matches_head_split(ref):
    blk = ref.backbone.blocks[0]
    H = blk.attn.num_heads
    w = blk.attn.qkv.weight.t()                                   # [1280, 3840]
    wp = vit._pad_qkv_weight(w, H)                                # [1280, 4608]
    assert tuple(wp.shape) == (fz.EMBED, 3 * H * fz.VIT_HEAD_DIM_PAD)
    v = wp.view(fz.EMBED, 3, H, fz.VIT_HEAD_DIM_PAD)
    assert torch.equal(v[..., : fz.VIT_HEAD_DIM], w.view(fz.EMBED, 3, H, fz.VIT_HEAD_DIM))
    assert torch.equal(v[..., fz.VIT_HEAD_DIM:], torch.zeros_like(v[..., fz.VIT_HEAD_DIM:]))
    bp = vit._pad_qkv_bias(blk.attn.qkv.bias, H).view(3, H, fz.VIT_HEAD_DIM_PAD)
    assert torch.equal(bp[..., : fz.VIT_HEAD_DIM], blk.attn.qkv.bias.view(3, H, fz.VIT_HEAD_DIM))
    assert bool((bp[..., fz.VIT_HEAD_DIM:] == 0).all())
    pw = vit._pad_proj_weight(blk.attn.proj.weight.t(), H).view(H, fz.VIT_HEAD_DIM_PAD, fz.EMBED)
    assert torch.equal(pw[:, : fz.VIT_HEAD_DIM], blk.attn.proj.weight.t().view(H, fz.VIT_HEAD_DIM, fz.EMBED))
    assert bool((pw[:, fz.VIT_HEAD_DIM:] == 0).all())
    # the padded columns of q/k/v produced through the fused-qkv layout are exactly zero
    x = torch.randn(1, 1, fz.N_TOKENS, fz.EMBED)
    q, k, vv = _emulate_create_qkv_heads(x @ wp + vit._pad_qkv_bias(blk.attn.qkv.bias, H), H)
    for t in (q, k, vv):
        assert tuple(t.shape) == (1, H, fz.N_TOKENS, fz.VIT_HEAD_DIM_PAD)
        assert bool((t[..., fz.VIT_HEAD_DIM:] == 0).all())
    # ... and equal the reference split of the unpadded qkv in the first 80 columns
    qr, kr, vr = (x[0] @ w + blk.attn.qkv.bias).reshape(1, fz.N_TOKENS, 3, H, fz.VIT_HEAD_DIM).permute(2, 0, 3, 1, 4)
    assert torch.equal(q[..., :80], qr) and torch.equal(k[..., :80], kr) and torch.equal(vv[..., :80], vr)


def _fused_block_torch(x4: torch.Tensor, blk, H: int) -> torch.Tensor:
    """fp64 torch model of ``vit._block_fused`` (4-D [1,1,192,1280] in/out)."""
    eps = blk.norm1.eps
    wqkv = vit._pad_qkv_weight(blk.attn.qkv.weight.t(), H).double()
    bqkv = vit._pad_qkv_bias(blk.attn.qkv.bias, H).double()
    wproj = vit._pad_proj_weight(blk.attn.proj.weight.t(), H).double()
    h = F.layer_norm(x4, (fz.EMBED,), blk.norm1.weight.double(), blk.norm1.bias.double(), eps)
    q, k, v = _emulate_create_qkv_heads(h @ wqkv + bqkv, H)                 # [1,16,192,96]
    scores = (q @ k.transpose(-1, -2)) * (fz.VIT_HEAD_DIM ** -0.5)          # scale = REAL head dim
    ctx = torch.softmax(scores, dim=-1) @ v                                # [1,16,192,96]
    ctx = _emulate_concat_heads(ctx)                                        # [1,1,192,1536]
    x4 = x4 + (ctx @ wproj + blk.attn.proj.bias.double())                   # dit(proj,+b,+res)
    h = F.layer_norm(x4, (fz.EMBED,), blk.norm2.weight.double(), blk.norm2.bias.double(), eps)
    h = F.gelu(h @ blk.mlp.fc1.weight.t().double() + blk.mlp.fc1.bias.double())
    return x4 + (h @ blk.mlp.fc2.weight.t().double() + blk.mlp.fc2.bias.double())  # dit(fc2,+b,+res)


def test_fused_vit_block_equals_reference_fp64(ref):
    blk = ref.backbone.blocks[0].double()
    x = torch.randn(1, fz.N_TOKENS, fz.EMBED, dtype=torch.float64)
    y_ref = blk(x)                                                          # reference Block.forward
    y_fused = _fused_block_torch(x.reshape(1, 1, fz.N_TOKENS, fz.EMBED), blk, blk.attn.num_heads)
    d = _maxd(y_fused.reshape(1, fz.N_TOKENS, fz.EMBED), y_ref)
    assert d <= 1e-9, d
    ref.backbone.blocks[0].float()


# =========================================================================== #
# 4. MANO head exact re-layouts + fused graph == reference (fp64)
# =========================================================================== #
def test_head_x0_constant(ref):
    td = ref.head.transformer
    x0 = fz.head_x0(td)
    assert tuple(x0.shape) == (1, 1, 1, fz.HEAD_DIM)
    token = torch.zeros(1, 1, 1)
    ref_x0 = td.to_token_embedding(token) + td.pos_embedding[:, :1]         # reference prologue
    assert torch.equal(x0.reshape(-1), ref_x0.reshape(-1))                  # 0 @ W is exactly 0
    # The legacy DEVICE chain rounds to bf16 three times (0@W -> +b -> +pos); the constant is
    # rounded once from the fp32 sum.  Where b and pos nearly cancel the legacy value carries
    # the rounding errors of both operands, so the bound is 2 bf16 ULP of the LARGER OPERAND
    # (0.5 ulp(b) + 0.5 ulp(pos) + 0.5 ulp(sum) + 0.5 ulp(sum)), not 1 ULP of the sum -- and
    # the constant is never farther from the fp32 value than the legacy chain (max and mean).
    bf = torch.bfloat16
    b, pos = td.to_token_embedding.bias.detach(), td.pos_embedding.detach()[0, 0]
    legacy = ((torch.zeros(fz.HEAD_DIM, dtype=bf) + b.to(bf)).float().to(bf) + pos.to(bf)).float()
    new = x0.reshape(-1).to(bf).float()
    ulp_operand = fz.bf16_ulp(torch.maximum(b.abs(), pos.abs()))
    assert bool(((legacy - new).abs() <= 2.0 * ulp_operand).all())
    exact = ref_x0.reshape(-1)
    assert _maxd(new, exact) <= _maxd(legacy, exact) + 1e-12
    assert float((new - exact).abs().mean()) <= float((legacy - exact).abs().mean())


def test_head_self_attention_is_v_for_one_token(ref):
    """N=1 => softmax over one key == 1.0 EXACTLY (x/x), so Attn(q,k,v) == v and the
    self-attention output is ``to_out(LN(x) @ Wv)`` with the V column slice of to_qkv."""
    assert torch.equal(torch.softmax(torch.randn(8, 1, 1) * 50, dim=-1), torch.ones(8, 1, 1))
    blk = ref.head.transformer.layers[0]
    wv = fz.head_sa_v_weight(blk)
    assert tuple(wv.shape) == (fz.HEAD_DIM, fz.HEAD_INNER)
    assert torch.equal(wv, blk.sa.to_qkv.weight.t()[:, 2 * fz.HEAD_INNER:])
    h = torch.randn(1, 1, fz.HEAD_DIM)
    d = _maxd(blk.sa.to_out(h @ wv), blk.sa(h))
    assert d <= 1e-5, d
    blk.double()
    d64 = _maxd(blk.sa.to_out(h.double() @ wv.double()), blk.sa(h.double()))
    assert d64 <= 1e-12, d64
    blk.float()


def test_head_fake_kv_weight_layout(ref):
    """``[0 | Wk | Wv]`` through the ``[Q|K|V]`` head split gives zero Q and the reference K/V heads."""
    blk = ref.head.transformer.layers[0]
    wf = fz.head_ca_kv_fake_weight(blk)
    assert tuple(wf.shape) == (fz.HEAD_CONTEXT, 3 * fz.HEAD_INNER)
    assert bool((wf[:, : fz.HEAD_INNER] == 0).all())
    assert torch.equal(wf[:, fz.HEAD_INNER:], blk.ca.to_kv.weight.t())
    assert wf.shape[1] % (3 * fz.HEAD_HEADS) == 0 and wf.shape[1] // (3 * fz.HEAD_HEADS) == fz.HEAD_DIM_HEAD
    ctx = torch.randn(1, 1, fz.N_TOKENS, fz.HEAD_CONTEXT, dtype=torch.float64)
    q0, k, v = _emulate_create_qkv_heads(ctx @ wf.double(), fz.HEAD_HEADS)
    assert bool((q0 == 0).all())
    kr, vr = blk.ca.to_kv.double()(ctx[0]).chunk(2, dim=-1)                # reference: chunk(2) then heads
    kr = kr.reshape(1, fz.N_TOKENS, fz.HEAD_HEADS, fz.HEAD_DIM_HEAD).permute(0, 2, 1, 3)
    vr = vr.reshape(1, fz.N_TOKENS, fz.HEAD_HEADS, fz.HEAD_DIM_HEAD).permute(0, 2, 1, 3)
    assert _maxd(k, kr) <= 1e-12 and _maxd(v, vr) <= 1e-12
    blk.ca.to_kv.float()
    # concat_heads on the one-token context == the reference "b h n d -> b n (h d)"
    ca = torch.randn(1, fz.HEAD_HEADS, 1, fz.HEAD_DIM_HEAD)
    assert torch.equal(_emulate_concat_heads(ca).reshape(1, 1, -1), ca.permute(0, 2, 1, 3).reshape(1, 1, -1))


def _fused_head_torch(context4: torch.Tensor, head) -> torch.Tensor:
    """fp64 torch model of ``mano_head._forward_fused``: returns dec [1,1,1,109]."""
    td = head.transformer
    x = fz.head_x0(td).double()
    eps = td.layers[0].norm_sa.eps
    for blk in td.layers:
        h = F.layer_norm(x, (fz.HEAD_DIM,), blk.norm_sa.weight.double(), blk.norm_sa.bias.double(), eps)
        v = h @ fz.head_sa_v_weight(blk).double()
        x = x + (v @ blk.sa.to_out.weight.t().double() + blk.sa.to_out.bias.double())
        h = F.layer_norm(x, (fz.HEAD_DIM,), blk.norm_ca.weight.double(), blk.norm_ca.bias.double(), eps)
        q = h @ blk.ca.to_q.weight.t().double()                                        # [1,1,1,512]
        q_h = q.reshape(1, 1, fz.HEAD_HEADS, fz.HEAD_DIM_HEAD).permute(0, 2, 1, 3)   # [1,8,1,64]
        _, k_h, v_h = _emulate_create_qkv_heads(context4 @ fz.head_ca_kv_fake_weight(blk).double(), fz.HEAD_HEADS)
        att = torch.softmax((q_h @ k_h.transpose(-1, -2)) * (fz.HEAD_DIM_HEAD ** -0.5), dim=-1) @ v_h
        ca = _emulate_concat_heads(att)                                                # [1,1,1,512]
        x = x + (ca @ blk.ca.to_out.weight.t().double() + blk.ca.to_out.bias.double())
        h = F.layer_norm(x, (fz.HEAD_DIM,), blk.norm_ff.weight.double(), blk.norm_ff.bias.double(), eps)
        h = F.gelu(h @ blk.ff.net[0].weight.t().double() + blk.ff.net[0].bias.double())
        x = x + (h @ blk.ff.net[2].weight.t().double() + blk.ff.net[2].bias.double())
    dec_w, dec_b = fz.head_dec_weight_bias(head)
    return x @ dec_w.double() + dec_b.double()


def test_fused_head_equals_reference_fp64(ref):
    head = ref.head.double()
    feats = torch.randn(1, fz.EMBED, fz.GRID_H, fz.GRID_W, dtype=torch.float64)   # reference input [B,C,h,w]
    context = feats.flatten(2).transpose(1, 2)                                      # [1,192,1280] (b (h w) c)
    tok = head.transformer(torch.zeros(1, 1, 1, dtype=torch.float64), context=context).squeeze(1)
    dec_ref = torch.cat([head.decpose(tok), head.decshape(tok), head.deccam(tok)], dim=-1)  # [1,109]
    dec = _fused_head_torch(context.reshape(1, 1, fz.N_TOKENS, fz.EMBED), head)
    assert tuple(dec.shape) == (1, 1, 1, fz.DEC_OUT)
    d = _maxd(dec.reshape(1, -1), dec_ref)
    assert d <= 1e-9, d
    # host_finalize on that dec reproduces the reference head output (init params + rot6d)
    out = mh.host_finalize(dec.reshape(1, -1).float(), (96, 10, 3), head.init_hand_pose.float(),
                           head.init_betas.float(), head.init_cam.float())
    ref_out = head(feats)
    assert tuple(out.shape) == (1, 157)
    assert _maxd(out, ref_out) <= 1e-4
    # numpy host_finalize == the original torch one
    dd = torch.randn(3, 109)
    args = ((96, 10, 3), head.init_hand_pose.float(), head.init_betas.float(), head.init_cam.float())
    assert _maxd(mh.host_finalize(dd, *args), mh.host_finalize_torch(dd, *args)) <= 2e-6
    ref.head.float()


def test_ds_head_relayouts_equal_reference_fp64(ref):
    """fp64 model of ``mano_head.forward_ds`` (TT_FUSED_HEAD=ds): host-folded layer-0 SA / query,
    ONE batched K/V projection split into (K_0, V_0, ..., K_5, V_5), the token replicated over a
    tile's 32 rows, the regressor N zero-padded to the DRAM banks -> equals the reference head."""
    head = ref.head.double()
    td = head.transformer
    feats = torch.randn(1, fz.EMBED, fz.GRID_H, fz.GRID_W, dtype=torch.float64)
    context = feats.flatten(2).transpose(1, 2)                                      # [1,192,1280]
    tok = td(torch.zeros(1, 1, 1, dtype=torch.float64), context=context).squeeze(1)
    dec_ref = torch.cat([head.decpose(tok), head.decshape(tok), head.deccam(tok)], dim=-1)
    x1, q0 = fz.head_layer0_constants(td)
    assert tuple(x1.shape) == (1, 1, 1, fz.HEAD_DIM) and tuple(q0.shape) == (1, fz.HEAD_HEADS, 1, fz.HEAD_DIM_HEAD)
    kv = context.reshape(1, 1, fz.N_TOKENS, fz.EMBED) @ fz.head_kv_all_weight(td)   # [1,1,192,6144]
    depth = len(td.layers)
    kvs = kv.reshape(1, fz.N_TOKENS, 2 * depth, fz.HEAD_HEADS, fz.HEAD_DIM_HEAD).permute(2, 0, 3, 1, 4)
    rep = lambda t: t.expand(*t.shape[:-2], 32, t.shape[-1])                       # noqa: E731
    x = rep(x1)
    eps = td.layers[0].norm_sa.eps
    for i, blk in enumerate(td.layers):
        if i > 0:
            h = F.layer_norm(x, (fz.HEAD_DIM,), blk.norm_sa.weight, blk.norm_sa.bias, eps)
            x = x + blk.sa.to_out(h @ fz.head_sa_v_weight(blk))
            h = F.layer_norm(x, (fz.HEAD_DIM,), blk.norm_ca.weight, blk.norm_ca.bias, eps)
            q_h = (h @ blk.ca.to_q.weight.t()).reshape(1, 32, fz.HEAD_HEADS, fz.HEAD_DIM_HEAD).permute(0, 2, 1, 3)
        else:
            q_h = rep(q0)
        att = torch.softmax((q_h @ kvs[2 * i].transpose(-1, -2)) * fz.HEAD_DIM_HEAD ** -0.5, dim=-1) @ kvs[2 * i + 1]
        x = x + blk.ca.to_out(att.permute(0, 2, 1, 3).reshape(1, 1, 32, fz.HEAD_INNER))
        h = F.layer_norm(x, (fz.HEAD_DIM,), blk.norm_ff.weight, blk.norm_ff.bias, eps)
        x = x + blk.ff(h)
    dec_w, dec_b = fz.head_dec_weight_bias(head)
    dec = x @ F.pad(dec_w, (0, 256 - fz.DEC_OUT)) + F.pad(dec_b, (0, 256 - fz.DEC_OUT))
    assert float(dec[..., fz.DEC_OUT:].abs().max()) == 0.0
    for r in (0, 17, 31):  # every replicated row carries the token
        assert _maxd(dec[0, 0, r:r + 1, : fz.DEC_OUT], dec_ref) <= 1e-9
    ref.head.float()


# =========================================================================== #
# 5. The fused DEVICE code, walked with a mock ttnn (shapes, validate rules, launches)
# =========================================================================== #
TILE = 32


def _pad32(n: int) -> int:
    return -(-n // TILE) * TILE


class FakeTensor:
    def __init__(self, shape, dtype, layout, mem, alive_set):
        self.shape = tuple(int(s) for s in shape)
        self.dtype = dtype
        self.layout = layout
        self.mem = mem
        self._alive = alive_set
        self._id = object()
        alive_set.add(self._id)

    # --- what the real code calls on tensors
    def memory_config(self):
        return self.mem

    def padded(self):
        if self.layout != "TILE" or len(self.shape) < 2:
            return self.shape
        return self.shape[:-2] + (_pad32(self.shape[-2]), _pad32(self.shape[-1]))

    def check(self):
        assert self._id in self._alive, "use of a deallocated tensor"
        return self

    def device(self):  # legacy vit.block asks the hidden tensor for its device
        return FakeDevice()

    # legacy path operators
    def __matmul__(self, other):
        return FAKE.matmul(self, other)

    def __add__(self, other):
        return FAKE.add(self, other)

    def __getitem__(self, i):
        FAKE.launches += 1
        self.check()
        return FAKE._new(self.shape[1:], self.dtype, self.layout, self.mem)


class Mem:
    def __init__(self, buffer_type, sharded=False):
        self.buffer_type = buffer_type
        self.sharded = sharded

    def __repr__(self):
        return f"Mem({self.buffer_type})"


class FakeTTNN(types.SimpleNamespace):
    """Just enough of ttnn for ``vit`` / ``mano_head`` (build + forward), enforcing the
    validate rules of this tree that a host cannot otherwise check.  Every op call counts
    as one device launch (``deallocate`` / build-time uploads do not)."""

    def __init__(self):
        super().__init__()
        self.launches = 0
        self.alive = set()
        self.calls = []
        self.uploads, self.copies, self.released = [], [], []
        self.bfloat16, self.bfloat8_b, self.float32, self.uint32 = "bf16", "bfp8", "fp32", "u32"
        self.TILE_LAYOUT, self.ROW_MAJOR_LAYOUT = "TILE", "RM"
        self.BufferType = types.SimpleNamespace(L1="L1", DRAM="DRAM")
        self.L1_MEMORY_CONFIG, self.DRAM_MEMORY_CONFIG = Mem("L1"), Mem("DRAM")
        self.MathFidelity = types.SimpleNamespace(LoFi="LoFi", HiFi2="HiFi2", HiFi4="HiFi4")
        self.experimental = types.SimpleNamespace(
            minimal_matmul=self.minimal_matmul,
            dit_minimal_matmul_addcmul_fused=self.dit_minimal_matmul_addcmul_fused,
            nlp_create_qkv_heads=self.nlp_create_qkv_heads,
            nlp_concat_heads=self.nlp_concat_heads,
        )
        self.transformer = types.SimpleNamespace(scaled_dot_product_attention=self.sdpa)

    # ---- construction (not launches)
    def _new(self, shape, dtype, layout, mem):
        return FakeTensor(shape, dtype, layout, mem, self.alive)

    def from_torch(self, t, device=None, layout="RM", dtype="bf16", memory_config=None, mesh_mapper=None):
        assert (dtype == "fp32") == (t.dtype == torch.float32), "host dtype must match the ttnn dtype"
        out = self._new(t.shape, dtype, layout, memory_config or Mem("DRAM"))
        out.on_device = device is not None
        self.uploads.append(out)
        return out

    # ---- model.py plumbing (persistent input buffer, trace release)
    def ReplicateTensorToMesh(self, device):
        return ("replicate", device)

    def copy_host_to_device_tensor(self, host_t, dev_t):
        host_t.check(); dev_t.check()
        assert host_t.on_device is False and dev_t.on_device is True
        # the persistent buffer's spec must match what the host builds (§5 triage row)
        assert (host_t.shape, host_t.dtype, host_t.layout) == (dev_t.shape, dev_t.dtype, dev_t.layout), (
            (host_t.shape, host_t.dtype, host_t.layout), (dev_t.shape, dev_t.dtype, dev_t.layout))
        self.copies.append((host_t, dev_t))

    def release_trace(self, device, trace_id):
        self.released.append(trace_id)

    def to_memory_config(self, t, mem):
        t.check()
        out = self._new(t.shape, t.dtype, t.layout, mem)
        return self._launch("reshard", out) if mem.sharded else out

    def deallocate(self, t):
        assert t._id in self.alive, "double deallocate"
        self.alive.discard(t._id)

    def CoreGrid(self, x, y):
        return (x, y)

    def CoreCoord(self, x, y):
        return types.SimpleNamespace(x=x, y=y)

    ShardStrategy = types.SimpleNamespace(BLOCK="BLOCK", WIDTH="WIDTH", HEIGHT="HEIGHT")
    ShardOrientation = types.SimpleNamespace(ROW_MAJOR="ROW_MAJOR", COL_MAJOR="COL_MAJOR")

    TensorMemoryLayout = types.SimpleNamespace(WIDTH_SHARDED="WIDTH_SHARDED", BLOCK_SHARDED="BLOCK_SHARDED")

    def num_cores_to_corerangeset(self, n, grid, row_wise=True):
        assert n <= grid.x * grid.y
        return ("cores", n)

    def ShardSpec(self, grid, shape, orientation):
        return types.SimpleNamespace(grid=grid, shape=tuple(shape), orientation=orientation)

    def MemoryConfig(self, layout, buffer_type, shard_spec=None):
        assert layout == "WIDTH_SHARDED" and buffer_type == "L1"
        n = shard_spec.grid[1]
        assert shard_spec.shape[1] % TILE == 0 and shard_spec.shape[0] % TILE == 0
        m = Mem("L1", sharded=True)
        m.width_shards = (n, shard_spec.shape)
        return m

    def create_sharded_memory_config(self, shape, *, core_grid, strategy, orientation):
        gx, gy = core_grid
        assert gx <= 12 and gy <= 10, "12x10 grid policy"
        assert strategy == "BLOCK" and shape[0] % (gy * TILE) == 0 and shape[1] % (gx * TILE) == 0
        return Mem("L1", sharded=True)

    def LayerNormShardedMultiCoreProgramConfig(self, *, compute_with_storage_grid_size, subblock_w, block_h,
                                               block_w, inplace):
        assert block_w % subblock_w == 0 and not inplace
        return types.SimpleNamespace(grid=tuple(compute_with_storage_grid_size), bh=block_h, bw=block_w)

    def sharded_to_interleaved(self, t, mem, output_dtype=None):
        t.check()
        assert t.mem.sharded and not mem.sharded
        return self._launch("s2i", self._new(t.shape, output_dtype or t.dtype, t.layout, mem))

    @staticmethod
    def _check_subblock(pcm, pcn, sh, sw):
        assert pcm % sh == 0 and pcn % sw == 0 and sh * sw <= 8, (pcm, pcn, sh, sw)
        assert sw == pcn or sh == 1, "out_subblock_w must equal per_core_N unless out_subblock_h == 1"

    def MatmulMultiCoreReuseMultiCast1DProgramConfig(self, *, compute_with_storage_grid_size, in0_block_w,
                                                     out_subblock_h, out_subblock_w, per_core_M, per_core_N,
                                                     fuse_batch, fused_activation, mcast_in0):
        self._check_subblock(per_core_M, per_core_N, out_subblock_h, out_subblock_w)
        g = compute_with_storage_grid_size
        assert g.x <= 12 and g.y <= 10, "12x10 grid policy"
        return types.SimpleNamespace(kind="1d", grid=g, ibw=in0_block_w, pcm=per_core_M, pcn=per_core_N)

    def MatmulMultiCoreReuseMultiCastProgramConfig(self, *, compute_with_storage_grid_size, in0_block_w,
                                                   out_subblock_h, out_subblock_w, per_core_M, per_core_N,
                                                   transpose_mcast, fused_activation):
        self._check_subblock(per_core_M, per_core_N, out_subblock_h, out_subblock_w)
        g = compute_with_storage_grid_size
        assert g.x <= 12 and g.y <= 10, "12x10 grid policy"
        return types.SimpleNamespace(kind="2d", grid=g, ibw=in0_block_w, pcm=per_core_M, pcn=per_core_N)

    @staticmethod
    def _check_pc(pc, M, K, N):
        Mt, Kt, Nt = _pad32(M) // TILE, _pad32(K) // TILE, _pad32(N) // TILE
        assert Kt % pc.ibw == 0, ("in0_block_w must divide K tiles", Kt, pc.ibw)
        if pc.kind == "1d":
            assert pc.pcm == Mt and -(-Nt // pc.pcn) <= pc.grid.x * pc.grid.y, (Mt, Nt, pc)
        else:
            assert pc.pcm * pc.grid.y >= Mt and pc.pcn * pc.grid.x >= Nt, (Mt, Nt, pc)

    def init_device_compute_kernel_config(self, arch, *, math_fidelity, fp32_dest_acc_en, packer_l1_acc):
        return types.SimpleNamespace(fidelity=math_fidelity, fp32=fp32_dest_acc_en)

    def MinimalMatmulConfig(self, *, M_block_size, K_block_size, N_block_size, subblock_h, subblock_w,
                            compute_with_storage_grid_size):
        assert M_block_size % subblock_h == 0 and N_block_size % subblock_w == 0
        assert subblock_h * subblock_w <= 4, "DST cap with fp32 accumulation is 4 tiles"
        return types.SimpleNamespace(M=M_block_size, K=K_block_size, N=N_block_size)

    def SDPAProgramConfig(self, *, compute_with_storage_grid_size, q_chunk_size, k_chunk_size, exp_approx_mode):
        assert q_chunk_size % TILE == 0 and k_chunk_size % TILE == 0
        return types.SimpleNamespace(q=q_chunk_size, k=k_chunk_size)

    # ---- launches
    def _launch(self, name, out):
        self.launches += 1
        self.calls.append(name)
        return out

    def layer_norm(self, x, *, weight, bias, epsilon=None, memory_config=None, program_config=None):
        x.check(); weight.check(); bias.check()
        if program_config is not None:  # sharded LN: block-sharded in/out, RM [1,1,C/32,32] gamma/beta
            assert x.mem.sharded and memory_config is not None and memory_config.sharded
            assert weight.layout == bias.layout == "RM" and weight.shape[-1] == 32
            assert weight.shape[-2] * 32 == x.shape[-1] == bias.shape[-2] * 32
            gx, gy = program_config.grid
            assert x.shape[-1] // TILE % gx == 0 and _pad32(x.shape[-2]) // TILE % gy == 0
            return self._launch("layer_norm", self._new(x.shape, x.dtype, x.layout, memory_config))
        assert weight.shape[-1] == x.shape[-1] == bias.shape[-1]
        assert epsilon is None or 0 < epsilon <= 1e-5  # fused path passes the reference eps; legacy none
        return self._launch("layer_norm", self._new(x.shape, x.dtype, x.layout, memory_config or x.mem))

    def _mm_checks(self, a, w, bias, ternary=None):
        a.check(); w.check()
        assert a.layout == "TILE" and w.layout == "TILE", "minimal_matmul requires TILE"
        assert a.dtype in ("bf16", "bfp8", "fp32") and w.dtype in ("bf16", "bfp8", "fp32")
        assert all(d == 1 for d in w.shape[:-2]), "weight leading dims must be 1"
        M, K, N = a.shape[-2], a.shape[-1], w.shape[-1]
        assert w.shape[-2] == K, (K, w.shape)
        if bias is not None:
            bias.check()
            assert bias.layout == "TILE" and bias.dtype in ("bf16", "bfp8")
            assert all(d == 1 for d in bias.shape[:-1]) and bias.shape[-1] == N, bias.shape
        if ternary is not None:
            res, ones = ternary
            res.check(); ones.check()
            assert res.layout == "TILE" and ones.layout == "TILE"
            assert res.shape[-2:] == (M, N), (res.shape, M, N)
            assert ones.shape[-2:] == (1, N) and all(d == 1 for d in ones.shape[:-1]), ones.shape
            assert res.dtype == w.dtype, f"dit fused: residual dtype {res.dtype} must equal weight dtype {w.dtype}"
            # Code invariant, NOT a validate rule of this tree: the minimal_matmul program factory
            # appends separate TensorAccessorArgs for the residual and the scale row, so mixed
            # placement would be legal.  The pairing is a precaution carried over from the rf-detr
            # BRIEF (DEVICE_VALIDATION.md §7); this asserts that ``_ones`` keeps doing what it says.
            assert res.mem.buffer_type == ones.mem.buffer_type, "_ones(): scale row placement should follow the residual (precaution)"
        return M, K, N

    def minimal_matmul(self, a, w, bias_tensor=None, *, config, memory_config=None, compute_kernel_config):
        _, _, N = self._mm_checks(a, w, bias_tensor)
        return self._launch("minimal_matmul", self._new(a.shape[:-1] + (N,), a.dtype, "TILE", memory_config or a.mem))

    def dit_minimal_matmul_addcmul_fused(self, a, w, scalar, res, ones, *, bias_tensor=None, config,
                                         memory_config=None, dtype=None, compute_kernel_config):
        assert scalar == 1.0
        _, _, N = self._mm_checks(a, w, bias_tensor, ternary=(res, ones))
        return self._launch("dit_fused", self._new(a.shape[:-1] + (N,), dtype or a.dtype, "TILE", memory_config or a.mem))

    def linear(self, a, w, *, bias=None, activation=None, memory_config=None, core_grid=None,
               program_config=None, compute_kernel_config=None, dtype=None):
        a.check(); w.check()
        assert a.layout == "TILE" and w.layout == "TILE" and a.shape[-1] == w.shape[-2]
        if program_config is not None:
            self._check_pc(program_config, a.shape[-2], a.shape[-1], w.shape[-1])
        if bias is not None:
            bias.check()
            assert len(bias.shape) >= 2 and all(d == 1 for d in bias.shape[:-1]), "row-vector bias [1,..,1,N]"
            assert _pad32(bias.shape[-1]) == _pad32(w.shape[-1])
        assert activation in (None, "gelu")
        if getattr(a.mem, "width_shards", None):  # width-sharded in0: 1D mcast-in0, one in0 block per shard
            n, (sh, sw) = a.mem.width_shards
            assert program_config is not None and program_config.kind == "1d" and program_config.ibw * TILE == sw
            assert sh == a.shape[-2] and n * sw == a.shape[-1]
        odt = {None: a.dtype, self.bfloat16: "bf16", self.float32: "fp32"}.get(dtype, dtype)
        return self._launch("linear", self._new(a.shape[:-1] + (w.shape[-1],), odt, "TILE", memory_config or a.mem))

    def matmul(self, a, b, core_grid=None):
        a.check(); b.check()
        assert a.shape[-1] == b.shape[-2]
        return self._launch("matmul", self._new(a.shape[:-1] + (b.shape[-1],), a.dtype, "TILE", a.mem))

    def add(self, a, b, memory_config=None):
        a.check(); b.check()
        shape = torch.broadcast_shapes(a.shape, b.shape)
        return self._launch("add", self._new(shape, a.dtype, a.layout, memory_config or a.mem))

    def gelu(self, x, fast_and_approximate_mode=False, memory_config=None):
        x.check()
        return self._launch("gelu", self._new(x.shape, x.dtype, x.layout, memory_config or x.mem))

    def reshape(self, x, shape, memory_config=None):
        x.check()
        assert math.prod(x.shape) == math.prod(shape), (x.shape, shape)
        return self._launch("reshape", self._new(shape, x.dtype, x.layout, memory_config or x.mem))

    def permute(self, x, dims):
        x.check()
        assert sorted(dims) == list(range(len(x.shape)))
        return self._launch("permute", self._new(tuple(x.shape[d] for d in dims), x.dtype, x.layout, x.mem))

    def tilize_with_zero_padding(self, x, *, memory_config=None, use_multicore=True):
        x.check()
        assert x.layout == "RM", "tilize input must be ROW_MAJOR"
        assert x.dtype in ("bf16", "fp32", "u32")
        return self._launch("tilize", self._new(x.shape, x.dtype, "TILE", memory_config or x.mem))

    def nlp_create_qkv_heads(self, x, *, num_heads, transpose_k_heads=True, memory_config=None):
        x.check()
        assert len(x.shape) == 4 and x.shape[1] == 1, x.shape
        assert x.layout == "TILE" and x.dtype in ("bf16", "bfp8", "fp32")
        assert x.padded()[2] % TILE == 0, "S must be tile aligned"
        assert x.padded()[3] % (3 * num_heads) == 0
        dh = x.padded()[3] // (3 * num_heads)
        B, S = x.shape[0], x.shape[2]
        mem = memory_config or x.mem
        q = self._new((B, num_heads, S, dh), x.dtype, "TILE", mem)
        k = self._new((B, num_heads, dh, S) if transpose_k_heads else (B, num_heads, S, dh), x.dtype, "TILE", mem)
        v = self._new((B, num_heads, S, dh), x.dtype, "TILE", mem)
        return self._launch("create_qkv_heads", (q, k, v))

    # hamer.tt_kernels generic_op tile permutes (exact; interleaved TILE in/out, batch 1)
    def head_split(self, x, num_heads, head_dim, n_out=3, memory_config=None, grid=None):
        x.check()
        assert len(x.shape) == 4 and x.shape[:2] == (1, 1) and x.layout == "TILE" and not x.mem.sharded
        assert x.shape[2] % TILE == 0 and head_dim % TILE == 0 and x.shape[3] == n_out * num_heads * head_dim
        mem = memory_config or x.mem
        outs = [self._new((1, num_heads, x.shape[2], head_dim), x.dtype, "TILE", mem) for _ in range(n_out)]
        return self._launch("head_split", outs)

    def head_concat(self, x, memory_config=None, grid=None):
        x.check()
        B, H, S, dh = x.shape
        assert B == 1 and S % TILE == 0 and dh % TILE == 0 and x.layout == "TILE"
        return self._launch("head_concat", self._new((1, 1, S, H * dh), x.dtype, "TILE", memory_config or x.mem))

    def addln_constants(self, gamma, beta, device):
        W = gamma.numel()
        up = lambda t: self.from_torch(t.reshape(1, 1, 1, W).to(torch.bfloat16).expand(1, 1, 32, W), device=device,
                                       layout="TILE", dtype="bf16")
        return {"gamma": up(gamma), "beta": up(beta), "W": W}

    def add_layernorm(self, x, y, consts, eps, out_memory_config=None, out_dtype=None, prefetch=None):
        x.check()
        assert x.mem.sharded and x.layout == "TILE" and x.dtype == "bf16" and x.shape[-1] == consts["W"]
        if y is not None:
            y.check()
            assert y.mem.sharded and y.shape == x.shape and y.dtype == x.dtype
        s = self._new(x.shape, x.dtype, "TILE", x.mem) if y is not None else None
        h = self._new(x.shape, out_dtype or "bf16", "TILE", out_memory_config or self.L1_MEMORY_CONFIG)
        self._launch("add_layernorm", h)
        return s, h

    def allocate_tensor_on_device(self, shape, dtype, layout, device, mem):
        return self._new(tuple(shape), dtype, layout, mem)

    def cmm_linear(self, a, w, b, out_memory_config, kb=8, slots=3, unicast=False, fidelity=None, defines=(),
                   layout="out", rotate=False, gelu=False, prefetch=None, a_heads=None, v1=False, ss=3, gelu_from=None,
                   helper_prefetch=None, addln=None, out_dtype=None, gelu_pf=None, idle_pf=None, idle_pf_remote=False):
        assert layout in ("out", "k2")  # round 11: k2 = the 2-way K-split fc2 (same output / epilogue contract)
        for pf_ in (gelu_pf, idle_pf, (addln or {}).get("prefetch2")):  # round 11: the next qkv's XmmPrefetch
            assert pf_ is None or isinstance(pf_, FakeXmmPrefetch)
        if gelu_from is not None:  # round 8: GELU fused into fc2 (helper cores); `a` is the freshly allocated buffer
            gelu_from.check()
            assert gelu_from.dtype == "bf16" and gelu_from.layout == "TILE" and a.shape == gelu_from.shape
        if addln is not None:  # round 8: the add+LN epilogue -> (new residual s, LN output h); x is consumed by the caller
            addln["x"].check()
            assert addln["x"].mem.sharded and addln["x"].shape[-1] == addln["consts"]["W"]
            a.check(); b.check()
            s_ = self._new(addln["x"].shape, "bf16", "TILE", addln["x"].mem)
            h_ = self._new(addln["x"].shape, addln.get("out_dtype") or "bf16", "TILE",
                           addln.get("out_mem") or Mem("L1"))
            self._launch("cmm", h_)  # (the cmm program with its add+LN epilogue)
            return s_, h_
        # hamer.tt_kernels.cmm_linear (TT_FUSED_CMM, default since round 5): packed weight, sharded output
        a.check(); b.check()
        assert isinstance(w, FakeCmmWeight) and w.kb == kb and a.layout == "TILE" and a.dtype in ("bf16", "bfp8")
        if a_heads is not None:
            H, D = a_heads
            assert len(a.shape) == 4 and a.shape[1] == H and a.shape[3] == D and H * D == w.K
            M = a.shape[2]
        else:
            assert a.shape[-1] == w.K
            M = a.shape[-2]
        assert out_memory_config.sharded and b.shape[-1] == w.N
        if prefetch is not None:
            assert prefetch.w is w
        return self._launch("cmm", self._new((1, 1, M, w.N), "bf16", "TILE", out_memory_config))

    def xmm_linear(self, a, w, b, rows, out_dtype=None, qkv=None, slots=3, fidelity=None, defines=(), out=None, la=1,
                   transposed=False, prefetch=None):
        # round 11: hamer.tt_kernels.xmm_linear (qkv): width-sharded in0, packed weight, head-split q / k / v outputs
        a.check(); b.check()
        assert isinstance(w, FakeCmmWeight) and a.layout == "TILE" and a.dtype in ("bf16", "bfp8")  # any placement (accessor)
        assert a.shape[-1] == w.K and b.shape[-1] == w.N and w.GX == 4 * rows and w.NT == 4
        assert prefetch is None or (isinstance(prefetch, FakeXmmPrefetch) and prefetch.w is w and transposed)
        H, Dh = qkv
        M = a.shape[-2]
        assert w.N == 3 * H * Dh
        outs = [self._new((1, H, M, Dh), out_dtype or "bf16", "TILE", Mem("L1")) for _ in range(3)]
        self._launch("xmm", outs[0])
        return outs

    def gelu_kernel(self, x, memory_config=None, grid=None, defines=(), fp32_dest=True, prefetch=None, out_dtype=None):
        # hamer.tt_kernels.gelu (custom erf-GELU generic_op, the default since round 5): TILE bf16 in, bf16 / bfp8 out
        x.check()
        assert x.layout == "TILE" and x.dtype == "bf16"
        return self._launch("gelu", self._new(x.shape, out_dtype or x.dtype, x.layout, memory_config or x.mem))

    def im2col_tile(self, img, memory_config=None, grid=None):
        img.check()
        assert img.shape == (1, 3, 256, 192) and img.dtype == "fp32" and img.layout == "RM"
        assert img.mem.buffer_type == "L1", "16 B-aligned NOC windows: the crop buffer must be in L1"
        return self._launch("im2col", self._new((1, 1, 192, 768), "fp32", "TILE", memory_config or Mem("L1")))

    def nlp_concat_heads(self, x, *, memory_config=None):
        x.check()
        assert len(x.shape) == 4 and x.layout == "TILE" and x.dtype in ("bf16", "bfp8", "fp32")
        B, H, S, dh = x.shape
        return self._launch("concat_heads", self._new((B, 1, S, H * dh), x.dtype, "TILE", memory_config or x.mem))

    def sdpa(self, q, k, v, *, attn_mask=None, is_causal, scale, program_config=None, memory_config=None):
        assert attn_mask is None, "never pass an explicit padding mask (wrong on Blackhole)"
        assert is_causal is False and scale is not None
        for t in (q, k, v):
            t.check()
            assert len(t.shape) == 4 and t.layout == "TILE" and t.dtype in ("bf16", "bfp8")
            assert t.shape[3] % TILE == 0, "no padding allowed on head_dim"
        assert k.shape == v.shape and q.shape[0] == k.shape[0] and q.shape[1] % k.shape[1] == 0 and q.shape[3] == k.shape[3]
        return self._launch("sdpa", self._new(q.shape, q.dtype, "TILE", memory_config or q.mem))


FAKE = FakeTTNN()


class FakeCmmWeight:
    def __init__(self, w_t, dev, GX, NT, kb, dtype, nbanks=0):
        self.K, self.N = w_t.shape
        assert self.N == GX * NT * 32 and (self.K // 32) % kb == 0 and ((self.K // 32) // kb) % 2 == 0
        self.kb, self.NT, self.GX, self.dtype = kb, NT, GX, dtype


class FakeCmmPrefetch:
    def __init__(self, dev, GX, GY, w, pairs, single=False):
        self.w, self.pairs = w, pairs

    def with_weight(self, w):
        o = FakeCmmPrefetch(None, 0, 0, w, self.pairs)
        return o

    def for_k2(self):
        return FakeCmmPrefetch(None, 0, 0, self.w, self.pairs)  # round 11: the k2 fc2's two senders per column

    def with_range(self, lo, hi):
        # round 8: the prefetch blocks split between the add+LN before fc1 and the GELU
        assert 0 <= lo <= hi <= 2 * self.pairs
        return FakeCmmPrefetch(None, 0, 0, self.w, self.pairs)


class FakeXmmPrefetch:
    def __init__(self, dev, w, rows, nblk):
        self.w, self.rows, self.nblk = w, rows, nblk

    def with_weight(self, w):
        return FakeXmmPrefetch(None, w, self.rows, self.nblk)

    def with_range(self, lo, hi):  # round 12: the qkv prefetch split between the proj and fc2 programs
        assert 0 <= lo <= hi <= self.nblk
        o = FakeXmmPrefetch(None, self.w, self.rows, self.nblk)
        o.rng = (lo, hi)
        return o


class FakeDevice:
    def compute_with_storage_grid_size(self):
        return types.SimpleNamespace(x=12, y=10)

    def arch(self):
        return "BLACKHOLE"


@pytest.fixture()
def fake(monkeypatch):
    global FAKE
    FAKE = FakeTTNN()
    monkeypatch.setattr(vit, "ttnn", FAKE)
    monkeypatch.setattr(mh, "ttnn", FAKE)
    monkeypatch.setattr(tk, "head_split", FAKE.head_split)
    monkeypatch.setattr(tk, "head_concat", FAKE.head_concat)
    monkeypatch.setattr(tk, "im2col_tile", FAKE.im2col_tile)
    monkeypatch.setattr(tk, "add_layernorm", FAKE.add_layernorm)
    monkeypatch.setattr(tk, "gelu", FAKE.gelu_kernel)
    monkeypatch.setattr(tk, "cmm_linear", FAKE.cmm_linear)
    monkeypatch.setattr(tk, "CmmWeight", FakeCmmWeight)
    monkeypatch.setattr(tk, "CmmPrefetch", FakeCmmPrefetch)
    monkeypatch.setattr(tk, "XmmPrefetch", FakeXmmPrefetch)
    monkeypatch.setattr(tk, "xmm_linear", FAKE.xmm_linear)
    monkeypatch.setattr(tk, "addln_constants", FAKE.addln_constants)
    return FAKE


@pytest.mark.parametrize("env", [dict(e, TT_FUSED_HEAD="minimal") for e in [
    {"TT_FUSED": "1"},                                                          # defaults: tuned, L1, device patch
    {"TT_FUSED": "1", "TT_FUSED_ADDLN": "0"},                                   # library add + LN + s2i
    {"TT_FUSED": "1", "TT_FUSED_IN0WS": "0"},                                   # addln -> interleaved qkv/fc1 input
    {"TT_FUSED": "1", "TT_FUSED_MM": "minimal", "TT_FUSED_SDPA": "default"},    # the p150a graph (420 launches)
    {"TT_FUSED": "1", "TT_FUSED_L1": "0"},                                      # DRAM placement
    {"TT_FUSED": "1", "TT_FUSED_FC2": "bfp8"},                                  # fc2 unfusable -> linear + add
    {"TT_FUSED": "1", "TT_FUSED_PATCH": "fp32w"},                               # fp32 patch weight / pos / ones
    {"TT_FUSED": "1", "TT_FUSED_PATCH": "host", "TT_FUSED_SDPA": "default"},    # legacy token upload
    {"TT_FUSED": "1", "TT_FUSED_MM": "linear", "TT_FUSED_HEAD_CONCAT": "permute", "TT_FUSED_FC1": "bf16"},
]])
def test_fused_device_graph_walk(ref, image, fake, env):
    """Build the fused parameters and run the fused ViT + head on the mock ttnn: every op's
    documented constraints hold, no tensor is used after deallocation, the input buffer is
    never freed, and the launch count is the device-validated one (2 ingest + 10/block + 1 LN;
    16/layer + 1 dec = 420 at depth 32/6)."""
    opts = FusedOptions.from_env(env)
    dev = FakeDevice()
    vp = vit.build_parameters_from_reference(ref, dev, fused=opts)
    hp = mh.build_parameters_from_reference(ref, dev, fused=opts)
    assert "fused" in vp and "fused" in hp and len(vp["blocks"]) == DEPTH and len(hp["layers"]) == HEAD_DEPTH
    fake.launches = 0
    fake.calls.clear()
    if opts.device_im2col:
        x_in = fake.from_torch(image.float(), device=dev, layout=fake.ROW_MAJOR_LAYOUT, dtype=fake.float32,
                               memory_config=fake.L1_MEMORY_CONFIG)
        ingest_launches = 2  # device im2col + patch matmul
    elif opts.device_patch_embed:
        host = fz.host_im2col(image)
        x_in = fake.from_torch(host, device=dev, layout=fake.ROW_MAJOR_LAYOUT, dtype=fake.float32)
        ingest_launches = 2
    else:
        host = ref.backbone.patch_embed(image)[0].to(torch.bfloat16)
        x_in = fake.from_torch(host, device=dev, layout=fake.TILE_LAYOUT, dtype=fake.bfloat16)
        ingest_launches = 2  # view reshape + pos add
    feat = vit.forward(x_in, vp, depth=DEPTH, fused=True)
    assert feat.shape == (1, 1, fz.N_TOKENS, fz.EMBED) and feat.dtype == "bf16" and feat.layout == "TILE"
    assert feat.mem.buffer_type == ("L1" if opts.l1 else "DRAM")
    vit_launches = fake.launches
    if opts.matmul == "tuned":
        per_block_extra = 2  # proj / fc2: linear(+bias) + residual add
        if opts.vit_shard and opts.l1 and opts.addln and opts.heads == "custom":
            per_block_extra = 0   # add + LN + s2i are ONE fused op (2 per block, counted in the 10)
            if vp["fused"].get("patch_lin") is None:
                ingest_launches += 1  # interleaved -> block-sharded residual stream
            # (round 8 patch_lin: the patch linear writes the sharded stream itself; pos is block 0's add+LN x)
            if vp["fused"].get("cmm"):
                per_block_extra = -1  # cmm proj reads the head-split SDPA output: no head_concat
                if vp["fused"].get("gelu_help"):
                    per_block_extra = -2  # round 8: the GELU runs inside the fc2 cmm program
                if vp["fused"].get("cmm_addln") and vp["fused"].get("patch_lin") is not None:
                    per_block_extra = -4  # round 8: both add+LNs are cmm epilogues (block 0's LN1 replaces the last LN)
                    if vp["blocks"][0]["attn"].get("qkv_xmm") is not None:
                        per_block_extra = -5  # round 11: qkv (xmm) writes the head-split q / k / v (no head_split)
                    if vp["fused"]["patch_lin"].get("cmm") is not None:
                        ingest_launches -= 1  # round 15: patch matmul + block 0's add+LN are ONE cmm program
        elif opts.vit_shard and opts.l1:
            per_block_extra += 2  # sharded LN -> interleaved copy before qkv / fc1
            ingest_launches += 1  # interleaved -> block-sharded residual stream
            ingest_launches += 1  # last LN sharded -> interleaved (counted with the ingest for brevity)
    else:
        per_block_extra = (1 if opts.wdtype("fc2") == "bfp8" or opts.matmul == "linear" else 0) + (1 if opts.matmul == "linear" else 0)
    assert vit_launches == ingest_launches + DEPTH * (10 + per_block_extra) + 1, fake.calls
    dec = mh.forward_device(feat, hp, dev, depth=HEAD_DEPTH, fused=True)
    head_launches = fake.launches - vit_launches
    per_layer_extra = 3 if opts.matmul == "linear" else 0  # 3 residual fusions -> linear + add each
    per_layer_extra += 1 if opts.head_concat == "permute" else 0  # permute + reshape instead of concat_heads
    # 16 per layer since the device pass: fc1 (+bias) and the exact gelu are separate launches
    # (ttnn.linear's fused gelu is not placement-neutral on the p150a, see mano_head._decoder_block_fused)
    assert head_launches == HEAD_DEPTH * (16 + per_layer_extra) + 1, fake.calls[-head_launches:]
    assert dec.shape == (1, 1, 1, fz.DEC_OUT)
    assert x_in._id in fake.alive, "the persistent input buffer must never be deallocated"
    assert hp["x0"]._id in fake.alive, "the constant x0 must never be deallocated"
    if opts.matmul == "minimal" and opts.wdtype("fc2") == "bf16":
        # the full-depth count (evaluation §5.1 said 414 with the fused-gelu linear; +6 since)
        full = 2 + 32 * 10 + 1 + 6 * 16 + 1
        assert full == 420
    if opts.sdpa == "32x192":
        assert vp["fused"]["sdpa_pc"].q == 32 and vp["fused"]["sdpa_pc"].k == 192
    else:
        assert vp["fused"]["sdpa_pc"] is None
    if opts.device_patch_embed:
        pt = vp["fused"]["patch"]
        want = "bf16" if opts.patch == "bf16w" else "fp32"
        assert pt["w"].dtype == pt["pos"].dtype == want and pt["pos"].shape == (1, 1, fz.N_TOKENS, fz.EMBED)
        assert pt["compute"].fidelity == "HiFi4" and pt["compute"].fp32 is True
    if vp["fused"].get("cmm"):
        assert vp["blocks"][0]["mlp"]["fc2_w"] is None and vp["blocks"][0]["mlp"]["fc2_cmm"].dtype == "bfp8"
        assert "cmm" in fake.calls and "concat_heads" not in fake.calls[:vit_launches]
    else:
        assert vp["blocks"][0]["mlp"]["fc2_w"].dtype == opts.wdtype("fc2")
    if vp["blocks"][0]["attn"].get("qkv_xmm") is not None:  # round 11: qkv as xmm_linear (packed bfp8 weight only)
        assert vp["blocks"][0]["attn"]["qkv_w"] is None and vp["blocks"][0]["attn"]["qkv_xmm"].dtype == "bfp8"
        assert "xmm" in fake.calls and "head_split" not in fake.calls[:vit_launches]
    else:
        assert vp["blocks"][0]["attn"]["qkv_w"].dtype == opts.wdtype("qkv_w")
    if vp["fused"].get("cmm"):
        assert vp["blocks"][0]["attn"]["proj_w"] is None and vp["blocks"][0]["attn"]["proj_cmm"].dtype == "bfp8"
    else:
        assert vp["blocks"][0]["attn"]["proj_w"].dtype == opts.wdtype("proj_w")
    assert vp["blocks"][0]["mlp"]["fc1_w"].dtype == ("bfp8" if opts.fc1 == "bfp8" else "bf16")
    assert vp["blocks"][0]["attn"]["qkv_b"].shape == (1, 1, 1, 3 * 16 * fz.VIT_HEAD_DIM_PAD)
    assert hp["layers"][0]["ca_kv_fake_w"].shape == (fz.HEAD_CONTEXT, 3 * fz.HEAD_INNER)
    assert hp["layers"][0]["sa_v_w"].shape == (fz.HEAD_DIM, fz.HEAD_INNER)
    assert hp["dec_b"].shape == (1, 1, 1, fz.DEC_OUT)


def test_legacy_graph_untouched_on_mock(ref, image, fake):
    """The legacy functions still run their 21-launch block / 32-launch layer graphs (and take
    no fused parameters)."""
    dev = FakeDevice()
    vp = vit.build_parameters_from_reference(ref, dev)
    hp = mh.build_parameters_from_reference(ref, dev)
    assert "fused" not in vp and "fused" not in hp and "sa_qkv_w" in hp["layers"][0]
    fake.launches = 0
    x_in = fake.from_torch(ref.backbone.patch_embed(image)[0].to(torch.bfloat16), device=dev,
                           layout=fake.TILE_LAYOUT, dtype=fake.bfloat16)
    monkey_grid = vit._MLP_GRID
    vit._MLP_GRID = None
    try:
        feat = vit.forward(x_in, vp, depth=DEPTH)
    finally:
        vit._MLP_GRID = monkey_grid
    assert feat.shape == (1, fz.N_TOKENS, fz.EMBED)
    assert fake.launches == 1 + DEPTH * 21 + 1
    n0 = fake.launches
    token = fake.from_torch(torch.zeros(1, 1, 1, dtype=torch.bfloat16), device=dev, layout="TILE", dtype="bf16")
    dec = mh.forward_device(feat, hp, dev, depth=HEAD_DEPTH, cached_token=(token,))
    assert fake.launches - n0 == 3 + HEAD_DEPTH * 32 + 2
    assert dec.shape == (1, 1, fz.DEC_OUT)


def test_head_cached_token_none_passthrough_when_fused(ref, image, fake):
    """``TtHamer._forward_device`` passes ``cached_token=(self._head_token,)`` on both paths; on
    the fused one ``_head_token`` is ``None`` (the head starts from the constant ``x0``) and
    ``forward_device`` must never look at it."""
    dev = FakeDevice()
    opts = FusedOptions.from_env({"TT_FUSED": "1", "TT_FUSED_HEAD": "minimal"})
    vp = vit.build_parameters_from_reference(ref, dev, fused=opts)
    hp = mh.build_parameters_from_reference(ref, dev, fused=opts)
    x_in = fake.from_torch(image.float(), device=dev, layout=fake.ROW_MAJOR_LAYOUT, dtype=fake.float32,
                           memory_config=fake.L1_MEMORY_CONFIG)
    feat = vit.forward(x_in, vp, depth=DEPTH, fused=True)
    dec = mh.forward_device(feat, hp, dev, depth=HEAD_DEPTH, cached_token=(None,), fused=True)
    assert dec.shape == (1, 1, 1, fz.DEC_OUT)


# =========================================================================== #
# 6. model.py plumbing on the mock (no device is ever opened: HAMER_USE_TT=0, device injected)
# =========================================================================== #
def _wrapper_on_fake(ref, fake, monkeypatch, env):
    """``TtHamer`` built without a chip (``HAMER_USE_TT=0``), then given the FakeDevice so the
    per-request plumbing (``_host_input`` / ``_upload_tokens`` / ``_forward_device`` guard) runs
    against the mock ``ttnn`` that ``model.py`` imports lazily."""
    from hamer.model import TtHamer

    monkeypatch.setitem(sys.modules, "ttnn", fake)
    for k in list(os.environ):
        if k.startswith("TT_FUSED"):
            monkeypatch.delenv(k, raising=False)
    for k, v in env.items():
        monkeypatch.setenv(k, v)
    m = TtHamer(ref, strict=False, use_trace=True)
    assert m.device is None and m._vit_params is None  # HAMER_USE_TT=0: nothing opened
    m.device = FakeDevice()
    m._is_mesh = False
    return m


@pytest.mark.parametrize("env, want", [
    ({"TT_FUSED": "1"}, ("fp32", "RM", (1, fz.IN_CHANS, fz.IMG_H, fz.IMG_W))),             # device im2col: raw crop
    ({"TT_FUSED": "1", "TT_FUSED_PATCH": "fp32w"}, ("fp32", "RM", (1, 1, fz.N_TOKENS, fz.PATCH_DIM))),
    ({"TT_FUSED": "1", "TT_FUSED_PATCH": "host"}, ("bf16", "TILE", (1, fz.N_TOKENS, fz.EMBED))),  # legacy CPU conv
    ({"TT_FUSED": "0"}, ("bf16", "TILE", (1, fz.N_TOKENS, fz.EMBED))),                        # legacy path
])
def test_host_input_selects_dtype_layout_per_knob(ref, image, fake, monkeypatch, env, want):
    m = _wrapper_on_fake(ref, fake, monkeypatch, env)
    host, dtype, layout = m._host_input(image)
    assert (dtype, layout, tuple(host.shape)) == want
    assert host.is_contiguous()
    if dtype == "fp32" and m.fused_opts.device_im2col:
        assert host.dtype == torch.float32 and torch.equal(host, image.float())
    elif dtype == "fp32":
        assert host.dtype == torch.float32 and torch.equal(host, fz.host_im2col(image))
    else:
        assert host.dtype == torch.bfloat16
        assert torch.equal(host, ref.backbone.patch_embed(image)[0].to(torch.bfloat16))


@pytest.mark.parametrize("env", [{"TT_FUSED": "1"}, {"TT_FUSED": "1", "TT_FUSED_HASH": "0"}, {"TT_FUSED": "0"}])
def test_upload_tokens_persistent_buffer_and_hash_cache(ref, image, fake, monkeypatch, env):
    """First upload allocates the persistent buffer; a repeated frame is served from the
    content cache (no upload) unless ``TT_FUSED_HASH=0``; a new frame of the same shape is
    copied INTO the same buffer (identity preserved -> the trace stays valid)."""
    m = _wrapper_on_fake(ref, fake, monkeypatch, env)
    use_cache = not (m.fused and not m.fused_opts.hash_cache)
    other = sample_input(batch=1, seed=11)

    buf = m._upload_tokens(image)
    assert buf.on_device and len(fake.uploads) == 1 and fake.copies == []
    assert (m._tokens_key is not None) == use_cache
    again = m._upload_tokens(image)                       # repeated frame
    assert again is buf
    assert len(fake.copies) == (0 if use_cache else 1)
    fresh = m._upload_tokens(other)                       # new content, same shape
    assert fresh is buf, "same shape -> copy into the persistent buffer, never a new allocation"
    assert len(fake.copies) == (1 if use_cache else 2)
    assert len(fake.uploads) == 1 + len(fake.copies)      # copies go through a host-only from_torch
    assert all(not h.on_device for h, _ in fake.copies)
    back = m._upload_tokens(image)
    assert back is buf and len(fake.copies) == (2 if use_cache else 3)
    assert fake.released == []


def test_upload_tokens_shape_change_reallocates_and_voids_trace(ref, image, fake, monkeypatch):
    """Legacy path (batching is legacy-only): a different batch size (re)allocates the buffer
    and releases the trace bound to the old one."""
    m = _wrapper_on_fake(ref, fake, monkeypatch, {"TT_FUSED": "0"})
    buf1 = m._upload_tokens(image)
    m._trace = (7, None)
    buf2 = m._upload_tokens(sample_input(batch=2, seed=5))
    assert buf2 is not buf1 and buf2.shape == (2, fz.N_TOKENS, fz.EMBED)
    assert m._trace is None and fake.released == [7]
    assert fake.copies == [] and len(fake.uploads) == 2


def test_fused_batch_guard_is_loud_even_when_not_strict(ref, fake, monkeypatch):
    """Review fix: the fused graph is batch-1 only (minimal_matmul folds B into M, validate
    compares the logical [-2] only, the head hard-codes the (1,1,H,dh) q reshape).  The guard
    must raise -- not become the non-strict CPU fallback that pins the harness accuracy to 100."""
    m = _wrapper_on_fake(ref, fake, monkeypatch, {"TT_FUSED": "1"})
    m._vit_params = {}  # pretend the upload succeeded so _forward_device reaches the guard
    m.strict = False
    two = sample_input(batch=2, seed=1)
    with pytest.raises(ValueError, match="batch 1"):
        m(two)
    assert fake.uploads == [] and fake.launches == 0, "the guard fires before any device work"
    # the legacy wrapper still accepts batch > 1 at the same point (falls through to the try)
    legacy = _wrapper_on_fake(ref, fake, monkeypatch, {"TT_FUSED": "0"})
    legacy._vit_params = None
    assert legacy._forward_device(two) is None  # no params -> documented non-strict None
    # and the direct fused ViT entry rejects it too
    opts = FusedOptions.from_env({"TT_FUSED": "1"})
    vp = vit.build_parameters_from_reference(ref, FakeDevice(), fused=opts)
    x2 = fake.from_torch(fz.host_im2col(two), device=FakeDevice(), layout=fake.ROW_MAJOR_LAYOUT, dtype=fake.float32)
    with pytest.raises(ValueError, match="batch 1"):
        vit.forward(x2, vp, depth=DEPTH, fused=True)


if __name__ == "__main__":  # plain-script fallback when pytest is unavailable
    sys.exit(pytest.main([__file__, "-q"]))


def test_u8_input_folding_is_exact(ref):
    """TT_FUSED_INPUT=u8: im2col(u - o) @ (a * W) + (pos_plus_bias + im2col(B) @ W) == im2col(x) @ W + pos_plus_bias
    for the normalized x of an 8-bit crop u (fp64 algebra; the padding stays zero in normalized space)."""
    from hamer.fused import (centred_pixels, host_im2col, patch_constants, patch_constants_u8,
                             pixels_from_normalized)
    from hamer.preprocess import normalize_raw
    g = torch.Generator().manual_seed(3)
    u = torch.randint(0, 256, (1, 3, 256, 192), generator=g, dtype=torch.uint8)
    x = normalize_raw(u)
    assert torch.equal(pixels_from_normalized(x), u.float())
    w, ppb = patch_constants(ref.backbone)
    w8, ppb8 = patch_constants_u8(ref.backbone)
    a = host_im2col(x).double() @ w.double() + ppb.double()
    v = centred_pixels(u)
    assert torch.equal(v.float() + torch.tensor([124., 116., 104.]).view(1, 3, 1, 1), u.float())
    b = host_im2col(v.float()).double() @ w8.double() + ppb8.double()
    assert float((a - b).abs().max()) < 2e-4 * float(a.abs().max())
    with pytest.raises(ValueError):
        pixels_from_normalized(torch.randn(1, 3, 256, 192))


def test_preprocess_box_resize_bit_identical():
    """Round 6: preprocess_pil resizes only the centre 192 columns (PIL resize box) -- bit-identical to the
    original resize-to-256x256 + flip + [32:224] slice, for random boxes (inside / crossing the frame), both hands."""
    import random
    import numpy as np
    from PIL import Image
    from hamer.preprocess import crop_bbox, preprocess_pil, W_SLICE
    media = Path(__file__).resolve().parents[2] / "media"
    rng = random.Random(0)
    ims = [Image.open(media / f"sample{i}.jpg").convert("RGB") for i in range(1, 5)]
    for _ in range(60):
        im = rng.choice(ims)
        x1, y1 = rng.uniform(-60, im.width - 10), rng.uniform(-60, im.height - 10)
        bb = (x1, y1, x1 + rng.uniform(8, 300), y1 + rng.uniform(8, 300))
        right = rng.random() < 0.5
        c256, meta = crop_bbox(im, bb, 2.0)
        if not right:
            c256 = c256.transpose(Image.FLIP_LEFT_RIGHT)
        want = np.asarray(c256)[:, W_SLICE:256 - W_SLICE].transpose(2, 0, 1)[None]
        got, meta2 = preprocess_pil(im, bb, is_right=right, raw=True)
        assert meta2 == meta and np.array_equal(got.numpy(), want)


def test_host_finalizer_tile_gather_bit_identical():
    """Round 13: ``HostFinalizer`` (row 0 gathered from the raw fp32 TILE bytes of a [1, 1, 32, 128] output, the
    init vector held by the object) == ``host_finalize`` on the untilized row, bit for bit."""
    rng = np.random.default_rng(13)
    split = (96, 10, 3)
    ip, ib, ic = (torch.from_numpy(rng.standard_normal((1, n)).astype(np.float32)) for n in split)
    init = mh.finalize_init_vector(ip, ib, ic)
    fin = mh.HostFinalizer(split, init, mh.HostFinalizer.tile_row0_index(109))
    for k in range(300):
        rows = (rng.standard_normal((32, 128)) * rng.choice([1e-3, 1.0, 40.0])).astype(np.float32)
        # tilize: 4 tiles of 32x32 along the width, each 4 faces of 16x16 (row-major faces)
        flat = rows.reshape(2, 16, 4, 2, 16).transpose(2, 0, 3, 1, 4).reshape(-1)
        a = fin(flat)
        b = mh.host_finalize(torch.from_numpy(rows[:1, :109].copy()), split, ip, ib, ic)
        c = mh.host_finalize(torch.from_numpy(rows[:1, :109].copy()), split, ip, ib, ic, init=init)
        assert torch.equal(a, b) and torch.equal(b, c)


def test_banded_resize_bit_identical():
    """Round 13: the threaded row-band resize (preprocess.RESIZE_BANDS) == the one-call resize, bit for bit, on random
    images / boxes (out-of-frame squares, odd rescale factors, left hands); the contiguous=False view has the same values."""
    from PIL import Image
    from hamer import preprocess as pp
    rng = np.random.default_rng(7)
    for k in range(120):
        W, H = int(rng.integers(64, 1200)), int(rng.integers(64, 1200))
        img = Image.fromarray(rng.integers(0, 256, (H, W, 3), dtype=np.uint8))
        x1, y1 = rng.uniform(0, W - 8), rng.uniform(0, H - 8)
        bbox = (x1, y1, x1 + rng.uniform(4, W - x1), y1 + rng.uniform(4, H - y1))
        right = bool(rng.integers(0, 2))
        rf = float(rng.choice([2.0, rng.uniform(0.6, 5.0)]))
        crop, _ = pp._square_crop(img, bbox, rf)
        one = pp.resize_crop_hw(crop, right, bands=1)
        for nb in (2, 3, 4):
            assert np.array_equal(one, pp.resize_crop_hw(crop, right, bands=nb)), (W, H, bbox, rf, right, nb)
        a = pp.preprocess_pil(img, bbox, rescale_factor=rf, is_right=right, raw=True)[0]
        b = pp.preprocess_pil(img, bbox, rescale_factor=rf, is_right=right, raw=True, contiguous=False)[0]
        assert a.is_contiguous() and torch.equal(a, b)


def test_native_crop_resize_bit_identical(monkeypatch):
    """Round 14 (P1): hamer/native/resize.c (crop + grey pad + PIL BILINEAR box resize + mirror in one call) == the PIL
    path, bit for bit: random frames, boxes inside / crossing / beyond the frame, odd rescale factors, both hands, plus
    the media crops; the bbox meta is identical too.  Skipped where no C compiler is available (the server then uses PIL)."""
    from PIL import Image
    from hamer import native
    from hamer import preprocess as pp
    if native.load() is None:
        pytest.skip(f"native preprocess unavailable: {native.STATUS}")
    rng = np.random.default_rng(14)
    cases = []
    media = Path(__file__).resolve().parents[2] / "media"
    import json
    for name, ents in json.loads((media / "bboxes.json").read_text()).items():
        im = Image.open(media / name).convert("RGB")
        cases += [(im, tuple(e["bbox"]), 2.0, bool(e.get("is_right", 1))) for e in ents]
    for k in range(300):
        W, H = int(rng.integers(16, 1100)), int(rng.integers(16, 1100))
        a = rng.integers(0, 256, (H, W, 3), dtype=np.uint8)
        if k % 2:
            a = np.cumsum(a, axis=1, dtype=np.uint8)
        w, h = rng.uniform(1.5, 1.3 * W), rng.uniform(1.5, 1.3 * H)
        x1, y1 = rng.uniform(-0.7 * w, W - 0.3 * w), rng.uniform(-0.7 * h, H - 0.3 * h)
        rf = float(rng.choice([2.0, 1.0, 0.75, rng.uniform(0.51, 5.0)]))
        cases.append((Image.fromarray(a), (x1, y1, x1 + w, y1 + h), rf, bool(k % 3)))
    n = 0
    for im, bb, rf, right in cases:
        if pp.hamer_bbox_size(bb, rf) < 2:
            continue
        got = native.crop_resize(np.asarray(im), bb, rf, right)
        crop, meta = pp._square_crop(im, bb, rf)
        want = pp.resize_crop_hw(crop, right, bands=1)
        if got is None:  # degenerate box: the PIL path handles it
            continue
        n += 1
        assert got[1] == meta and np.array_equal(got[0], want), (im.size, bb, rf, right)
        # round 15: the zero-copy path (the frame's RGBX buffer read in place through Pillow's Arrow export) == the same bytes
        zc = native.crop_resize_image(im, bb, rf, right)
        assert zc is not None and zc[1] == meta and np.array_equal(zc[0], want), ("zero-copy", im.size, bb, rf, right)
    assert n > 250
    # preprocess_pil end to end (raw u8 CHW, raw=False normalized fp32), native vs HAMER_PRE_NATIVE=0
    for im, bb, rf, right in cases[:8] + cases[40:48]:
        if pp.hamer_bbox_size(bb, rf) < 2:
            continue
        monkeypatch.delenv("HAMER_PRE_NATIVE", raising=False)
        a = [pp.preprocess_pil(im, bb, rescale_factor=rf, is_right=right, raw=r) for r in (True, False)]
        monkeypatch.setenv("HAMER_PRE_NATIVE", "0")
        b = [pp.preprocess_pil(im, bb, rescale_factor=rf, is_right=right, raw=r) for r in (True, False)]
        assert a[0][0].is_contiguous() and tuple(a[0][0].shape) == (1, 3, 256, 192)
        for x, y in zip(a, b):
            assert torch.equal(x[0], y[0]) and x[1] == y[1]


def test_tiled_input_im2col_bit_identical():
    """Round 14 (TT_FUSED_TILE_IN): the host-built TILE-order im2col of the centred crop (index table and the native C
    writer) == ttnn's host tilize of ``host_im2col(centred_pixels(u))``, for contiguous CHW crops and strided HWC views."""
    import ttnn
    from hamer import native
    from hamer.fused import PIXEL_OFFSET, centred_pixels, host_im2col, im2col_tile_index
    from hamer.model import TtHamer
    idx = im2col_tile_index()
    io = {"tile_idx": idx, "v_ext": torch.zeros(3 * 256 * 192 + 1, dtype=torch.bfloat16),
          "in_t": torch.empty(192 * 768, dtype=torch.bfloat16), "im2col": native.im2col_tile_fn(),
          "offset": torch.tensor(PIXEL_OFFSET, dtype=torch.bfloat16).view(1, 3, 1, 1)}
    g = torch.Generator().manual_seed(3)
    for k in range(6):
        hwc = torch.randint(0, 256, (256, 192, 3), generator=g, dtype=torch.uint8)
        u = hwc.permute(2, 0, 1).unsqueeze(0)
        if k % 2:
            u = u.contiguous()
        m = host_im2col(centred_pixels(u).float()).to(torch.bfloat16)
        h = ttnn.from_torch(m, layout=ttnn.TILE_LAYOUT, dtype=ttnn.bfloat16)
        want = torch.utils.dlpack.from_dlpack(h.host_buffer().get_shard(ttnn.MeshCoordinate(0, 0))).view(torch.bfloat16).view(-1).clone()
        TtHamer._write_input(None, io, u, force_torch=True)
        assert torch.equal(io["in_t"], want)
        if io["im2col"] is not None:
            io["in_t"].zero_()
            TtHamer._write_input(None, io, u)
            assert torch.equal(io["in_t"], want)