File size: 40,713 Bytes
d710727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0a01d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d710727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0a01d0
 
d710727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Packed on-disk storage for LTX-2.5 encoder precision plans.

`ltx_fake_quantize` and `ltx_gptq` store quantized values expanded back to
BF16, so a 4.5-bit plan still costs 26 GB on disk. This module is the missing
second half: the same values in their native widths.

The format is one safetensors file. A raw tensor keeps its checkpoint name. A
quantized tensor is split into named parts:

* nvfp4 - ``{name}::nvfp4_codes`` (uint8, two 4-bit codes per byte, even
  column in the low nibble; a code is ``sign << 3 | magnitude`` indexing
  ``E2M1_LEVELS``), ``{name}::nvfp4_group_scale`` (float8_e4m3fn,
  [out, in/16]), ``{name}::nvfp4_global_scale`` (float32 scalar), and - when
  the GPTQ path smoothed the tensor - ``{name}::pre_quant_scale`` (float32,
  [in], the AWQ scale the stored weight was *divided* by).
* int8 - ``{name}::int8`` and ``{name}::int8_scale`` (float32, [out]).

Exactness is the design constraint, not an aspiration: unpacking replays the
producers' arithmetic - ``(sign * level) * effective`` then the pre-scale
division, in float32, cast to the stored dtype last - so a packed tensor
dequantizes to the same values the BF16 fake-quant file would have carried.
The one tolerated difference is that int8 cannot store a negative zero, so a
``-0.0`` produced by ``round()`` collapses to ``+0.0``; ``torch.equal`` treats
the two as equal and the packer counts them rather than hiding them.

Group scales are recorded as the exact float8 bytes the quantizer used, not
recomputed from the output. Recomputing them is impossible in general: the
GPTQ column loop derives each group's scale from weights that were already
compensated, and the AWQ division afterwards destroys the grid alignment that
recovery would need. That is why packing happens inside the build
(`ltx_gptq --packed-output`) instead of as a post-pass over the artifact.
"""

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

import torch

sys.path.insert(0, str(Path(__file__).resolve().parent))

# --- inlined from ltx_fake_quantize -----------------------------------------
#: The eight magnitudes E2M1 can represent, and the midpoints between them that
#: decide which one a value rounds to.
E2M1_LEVELS = (0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0)
E2M1_MIDPOINTS = (0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5.0)
NVFP4_GROUP = 16
#: E4M3's largest finite value; the per-tensor scale is chosen so the per-group
#: scales divided by it land inside this range.
E4M3_MAX = 448.0
GPU_TENSOR_LIMIT = 1_000_000_000

# --- inlined from ltx_prompt_embedding_gate ---------------------------------
#: Lightricks re-exported the encoder under the naming Google used before the
#: `gemma4_unified` port landed in `transformers`. Without these rules
#: `from_pretrained` reports every checkpoint tensor UNEXPECTED and every
#: parameter MISSING, and hands back a randomly initialised model that looks
#: like it loaded.
LTX_RENAMES = [
    (r"^model\.", "language_model."),
    (r"^vision_model\.", "embed_vision."),
    (r"^audio_projector\.", "embed_audio."),
    (r"^multi_modal_projector\.embedding_projection",
     "embed_vision.multimodal_embedder.embedding_projection"),
]


def register_ltx_renames() -> None:
    from transformers.conversion_mapping import (
        get_checkpoint_conversion_mapping,
        register_checkpoint_conversion_mapping,
    )
    from transformers.core_model_loading import WeightRenaming

    existing = get_checkpoint_conversion_mapping("gemma4_unified") or []
    rules = [WeightRenaming(source_patterns=s, target_patterns=t)
             for s, t in LTX_RENAMES]
    register_checkpoint_conversion_mapping(
        "gemma4_unified", rules + existing, overwrite=True)
# ----------------------------------------------------------------------------

FORMAT_VERSION = "haverbex-packed-v1"

#: Part suffixes. `::` cannot appear in a checkpoint tensor name, so packed
#: parts can never collide with a raw tensor.
CODES = "::nvfp4_codes"
GROUP_SCALE = "::nvfp4_group_scale"
GLOBAL_SCALE = "::nvfp4_global_scale"
PRE_SCALE = "::pre_quant_scale"
INT8 = "::int8"
INT8_SCALE = "::int8_scale"

_SUFFIXES = (CODES, GROUP_SCALE, GLOBAL_SCALE, PRE_SCALE, INT8, INT8_SCALE)

#: Parts indexed by output row, so a chunked read can slice them. The other two
#: are not: `GLOBAL_SCALE` is a scalar and `PRE_SCALE` is per *input* channel.
_ROW_SLICEABLE = (CODES, GROUP_SCALE, INT8, INT8_SCALE)

#: Part suffix -> buffer name on a resident module. `::` is illegal in a
#: `register_buffer` name, so the mapping cannot be derived.
_BUFFER_NAMES = {CODES: "codes", GROUP_SCALE: "group_scale",
                 GLOBAL_SCALE: "global_scale", PRE_SCALE: "pre_scale",
                 INT8: "ints", INT8_SCALE: "int8_scale"}


def nibble_pack(codes: torch.Tensor) -> torch.Tensor:
    """[out, in] uint8 (values 0..15) -> [out, in/2], even column low nibble."""
    if codes.shape[-1] % 2:
        raise ValueError(f"odd inner width {codes.shape[-1]} cannot nibble-pack")
    return codes[..., 0::2] | (codes[..., 1::2] << 4)


def nibble_unpack(packed: torch.Tensor) -> torch.Tensor:
    out = torch.empty(*packed.shape[:-1], packed.shape[-1] * 2, dtype=torch.uint8,
                      device=packed.device)
    out[..., 0::2] = packed & 0x0F
    out[..., 1::2] = packed >> 4
    return out


def encode_nvfp4_column(normalized: torch.Tensor, codes: torch.Tensor) -> torch.Tensor:
    """4-bit code for one already-bucketized column: sign bit over magnitude.

    `torch.sign` maps an exact zero to 0, which multiplies out to ``+0.0``; the
    encoding gives it sign bit 0 so decode lands on the same ``+0.0``.
    """
    return codes.to(torch.uint8) | ((normalized < 0).to(torch.uint8) << 3)


def decode_nvfp4(codes: torch.Tensor, group_scale: torch.Tensor,
                 global_scale: torch.Tensor,
                 pre_scale: torch.Tensor | None) -> torch.Tensor:
    """Replay of `NVFP4Column.quantize` / `quantize_nvfp4`, in float32.

    `levels` is built on the codes' device: at load time everything is on the
    CPU, but a `PackedLinear` decodes wherever its weight lives.
    """
    levels = torch.tensor(E2M1_LEVELS, dtype=torch.float32, device=codes.device)
    sign = torch.where((codes & 0x8) != 0, -1.0, 1.0)
    magnitude = levels[(codes & 0x7).long()]

    effective = group_scale.float() * global_scale.float()
    effective = torch.where(effective > 0, effective, torch.ones_like(effective))
    out_features, in_features = codes.shape
    grid = (sign * magnitude).reshape(out_features, in_features // NVFP4_GROUP,
                                      NVFP4_GROUP)
    grid = (grid * effective.unsqueeze(-1)).reshape(out_features, in_features)
    if pre_scale is not None:
        grid = grid / pre_scale.float()
    return grid


def decode_int8(ints: torch.Tensor, scale: torch.Tensor,
                pre_scale: torch.Tensor | None) -> torch.Tensor:
    """Replay of `quantize_int8` / `Int8Column.quantize`, in float32."""
    grid = ints.float() * scale.float().unsqueeze(-1)
    if pre_scale is not None:
        grid = grid / pre_scale.float()
    return grid


def pack_rtn_nvfp4(w: torch.Tensor) -> tuple[torch.Tensor, dict]:
    """`ltx_fake_quantize.quantize_nvfp4` with the codes and scales kept.

    Returns the dequantized float32 tensor (identical to what the fake
    quantizer produces) and the packed parts.
    """
    out_features, in_features = w.shape
    if in_features % NVFP4_GROUP:
        raise ValueError(f"nvfp4 needs a multiple of {NVFP4_GROUP}, got {in_features}")
    amax = w.abs().amax()
    if amax == 0:
        raise ValueError("all-zero tensor should be stored raw, not packed")
    global_scale = amax / (E2M1_LEVELS[-1] * E4M3_MAX)

    groups = w.reshape(out_features, in_features // NVFP4_GROUP, NVFP4_GROUP)
    group_amax = groups.abs().amax(dim=-1, keepdim=True)
    scale = (group_amax / E2M1_LEVELS[-1] / global_scale).to(torch.float8_e4m3fn)
    effective = scale.float() * global_scale
    effective = torch.where(effective > 0, effective, torch.ones_like(effective))

    levels = torch.tensor(E2M1_LEVELS, device=w.device, dtype=w.dtype)
    midpoints = torch.tensor(E2M1_MIDPOINTS, device=w.device, dtype=w.dtype)
    normalized = groups / effective
    codes = torch.bucketize(normalized.abs(), midpoints, out_int32=True)
    dequant = (torch.sign(normalized) * levels[codes] * effective).reshape(
        out_features, in_features)

    packed_codes = (codes.to(torch.uint8)
                    | ((normalized < 0).to(torch.uint8) << 3)).reshape(
                        out_features, in_features)
    parts = {
        CODES: nibble_pack(packed_codes).cpu(),
        GROUP_SCALE: scale.squeeze(-1).cpu(),
        GLOBAL_SCALE: global_scale.detach().float().reshape(1).cpu(),
    }
    return dequant, parts


def pack_rtn(writer: PackWriter, name: str, tensor: torch.Tensor,
             width: float, device: str) -> torch.Tensor:
    """`ltx_fake_quantize.quantize_tensor`, but keeping the codes.

    Returns the fake-quantized tensor in the original dtype so the caller's
    BF16 write path is unchanged. Same device policy as the original: a tensor
    whose float32 form exceeds `GPU_TENSOR_LIMIT` is quantized on the CPU.
    """
    original = tensor.dtype
    where = "cpu" if tensor.numel() * 4 > GPU_TENSOR_LIMIT else device
    w = tensor.to(where, torch.float32)
    if width == 4.5:
        if w.abs().amax() == 0:
            # quantize_nvfp4 returns an all-zero tensor unchanged; store it raw
            writer.store_raw(name, tensor)
            return tensor
        dequant, parts = pack_rtn_nvfp4(w)
        result = dequant.to("cpu", original)
        writer.store_nvfp4(name, parts, result)
    else:
        dequant, parts = pack_rtn_int8(w)
        result = dequant.to("cpu", original)
        writer.store_int8(name, parts, result)
    return result


def pack_rtn_int8(w: torch.Tensor) -> tuple[torch.Tensor, dict]:
    """`ltx_fake_quantize.quantize_int8` with the integers kept."""
    scale = w.abs().amax(dim=-1, keepdim=True) / 127.0
    scale = torch.where(scale > 0, scale, torch.ones_like(scale))
    ints = (w / scale).round().clamp_(-127, 127)
    dequant = ints * scale
    parts = {
        INT8: ints.to(torch.int8).cpu(),
        INT8_SCALE: scale.squeeze(-1).float().cpu(),
    }
    return dequant, parts


class PackWriter:
    """Accumulates packed parts and raw tensors, then writes one file.

    Held in RAM rather than streamed: the whole point of the format is that
    the payload is ~8.5 GB, which fits beside the build. Every quantized store
    verifies round-trip equality against the reference tensor before
    accepting it - a packed file that does not reproduce its builder's values
    must not be creatable through this class.
    """

    def __init__(self) -> None:
        self.tensors: dict[str, torch.Tensor] = {}
        self.kinds: dict[str, str] = {}
        self.negative_zero_collapses = 0

    def store_raw(self, name: str, tensor: torch.Tensor) -> None:
        if any(s in name for s in _SUFFIXES):
            raise ValueError(f"raw name collides with a part suffix: {name}")
        self.tensors[name] = tensor.detach().contiguous().cpu()
        self.kinds[name] = "raw"

    def _verify(self, name: str, reference: torch.Tensor) -> None:
        got = unpack_tensor(self.tensors, name, self.kinds[name],
                            reference.dtype)
        if not torch.equal(got, reference.cpu()):
            diff = (got.float() - reference.float().cpu()).abs()
            raise SystemExit(
                f"packed round-trip mismatch on {name}: max {diff.max():.3e} at "
                f"{int(diff.argmax())} - refusing to write a lossy pack")
        signs = got.signbit() != reference.cpu().signbit()
        self.negative_zero_collapses += int(signs.sum())

    def store_nvfp4(self, name: str, parts: dict, reference: torch.Tensor,
                    pre_scale: torch.Tensor | None = None) -> None:
        for suffix, tensor in parts.items():
            self.tensors[name + suffix] = tensor.contiguous()
        if pre_scale is not None:
            self.tensors[name + PRE_SCALE] = pre_scale.detach().float().cpu()
        self.kinds[name] = "nvfp4"
        self._verify(name, reference)

    def store_int8(self, name: str, parts: dict, reference: torch.Tensor,
                   pre_scale: torch.Tensor | None = None) -> None:
        for suffix, tensor in parts.items():
            self.tensors[name + suffix] = tensor.contiguous()
        if pre_scale is not None:
            self.tensors[name + PRE_SCALE] = pre_scale.detach().float().cpu()
        self.kinds[name] = "int8"
        self._verify(name, reference)

    def save(self, path: Path, metadata: dict[str, str]) -> None:
        from safetensors.torch import save_file
        payload = {
            "format": FORMAT_VERSION,
            "kinds": json.dumps(self.kinds, separators=(",", ":")),
            "negative_zero_collapses": str(self.negative_zero_collapses),
            **metadata,
        }
        path.parent.mkdir(parents=True, exist_ok=True)
        save_file(self.tensors, str(path), metadata=payload)


def unpack_tensor(tensors: dict, name: str, kind: str,
                  dtype: torch.dtype) -> torch.Tensor:
    if kind == "raw":
        return tensors[name]
    pre = tensors.get(name + PRE_SCALE)
    if kind == "nvfp4":
        grid = decode_nvfp4(nibble_unpack(tensors[name + CODES]),
                            tensors[name + GROUP_SCALE],
                            tensors[name + GLOBAL_SCALE],
                            pre)
    elif kind == "int8":
        grid = decode_int8(tensors[name + INT8], tensors[name + INT8_SCALE], pre)
    else:
        raise ValueError(f"unknown kind {kind!r} for {name}")
    return grid.to(dtype)


class PackedCheckpoint:
    """Read-side API mirroring `safetensors.safe_open` for packed files."""

    def __init__(self, path: Path | str) -> None:
        from safetensors import safe_open
        self.path = Path(path)
        self._file = safe_open(str(self.path), framework="pt")
        self.metadata = self._file.metadata() or {}
        if self.metadata.get("format") != FORMAT_VERSION:
            raise SystemExit(
                f"{path}: format {self.metadata.get('format')!r}, "
                f"this reader speaks {FORMAT_VERSION}")
        self.kinds: dict[str, str] = json.loads(self.metadata["kinds"])
        self._present = set(self._file.keys())

    def __enter__(self) -> PackedCheckpoint:
        return self

    def __exit__(self, *exc) -> None:
        return None

    def keys(self) -> list[str]:
        return list(self.kinds)

    def get_tensor(self, name: str, dtype: torch.dtype = torch.bfloat16):
        kind = self.kinds[name]
        if kind == "raw":
            return self._file.get_tensor(name)
        parts = {name + s: self._file.get_tensor(name + s)
                 for s in _SUFFIXES if name + s in self._present}
        return unpack_tensor(parts, name, kind, dtype)

    def get_parts(self, name: str) -> dict:
        """The stored parts of a packed tensor, keyed by bare suffix, undecoded.

        What a resident module needs: the bytes as written, with no decode and
        no reassembly.
        """
        return {s: self._file.get_tensor(name + s)
                for s in _SUFFIXES if name + s in self._present}

    def row_count(self, name: str) -> int:
        """Output rows of a packed tensor, without decoding it."""
        kind = self.kinds[name]
        key = name if kind == "raw" else name + (CODES if kind == "nvfp4" else INT8)
        return self._file.get_slice(key).get_shape()[0]

    def get_tensor_rows(self, name: str, start: int, stop: int,
                        dtype: torch.dtype = torch.bfloat16):
        """Output rows `[start, stop)` of a tensor, decoding only those rows.

        Lets a caller work through a table that does not fit beside the model -
        the LTX aggregates are 3.08 GB each in float32. Row-sliced parts are
        the ones indexed by output channel; the global scale is a scalar and
        the AWQ pre-scale is per *input* channel, so both are read whole.
        """
        kind = self.kinds[name]
        if kind == "raw":
            return self._file.get_slice(name)[start:stop].to(dtype)
        parts = {}
        for suffix in _SUFFIXES:
            key = name + suffix
            if key not in self._present:
                continue
            parts[key] = (self._file.get_slice(key)[start:stop]
                          if suffix in _ROW_SLICEABLE
                          else self._file.get_tensor(key))
        return unpack_tensor(parts, name, kind, dtype)


def open_maybe_packed(path: Path | str):
    """`safe_open` for BF16 checkpoints, `PackedCheckpoint` for packed ones.

    Both expose `keys()` and `get_tensor(name)`, which is all the aggregate
    and asset readers use. Detection is by content, not extension: a packed
    file carries `format` metadata that a plain checkpoint does not.
    """
    from safetensors import safe_open
    handle = safe_open(str(path), framework="pt")
    if (handle.metadata() or {}).get("format") == FORMAT_VERSION:
        return PackedCheckpoint(path)
    return handle


def install_resident(model, reader: PackedCheckpoint, wanted: dict, rename,
                     device: str | None = None, fold_pre_scale: bool = False):
    """Replace the quantized modules of a meta-device model with packed ones.

    Two passes, because a module has to exist before its bias can be fed into
    it: first swap every module whose weight is packed, then feed the raw
    tensors - norms, layer scalars, and any bias - into whatever module now
    sits at that path.

    The coverage guard is the same one the dequantized path uses, and it is
    what makes a silent rename failure impossible: every parameter the model
    declared must be accounted for, either by a swap or by a raw feed.
    """
    from accelerate.utils import set_module_tensor_to_device

    if device is None:
        device = "cuda" if torch.cuda.is_available() else "cpu"

    swapped, raw_names = set(), []
    for source_name in reader.keys():  # noqa: SIM118 - PackedCheckpoint, not a dict
        param_name = rename(source_name)
        if param_name not in wanted:
            continue  # aggregates and asset blobs live outside the module tree
        if reader.kinds[source_name] == "raw":
            raw_names.append((source_name, param_name))
            continue
        if not param_name.endswith(".weight"):
            raise SystemExit(
                f"{source_name} is packed but renames to {param_name}, which is "
                "not a module weight; a resident build has nowhere to put it")
        path = param_name[: -len(".weight")]
        module = model.get_submodule(path)
        parts = {k: v.to(device) for k, v in reader.get_parts(source_name).items()}

        if isinstance(module, torch.nn.Embedding):
            # Subclasses carry forward behaviour a bare gather would lose -
            # Gemma's scales by ~sqrt(hidden). Anything else must stop the build.
            extra = set(dict(module.named_buffers()))- {"embed_scale"}
            if type(module) is not torch.nn.Embedding and extra:
                raise SystemExit(
                    f"{path} is a {type(module).__name__} carrying {sorted(extra)}; "
                    "PackedEmbedding only reproduces embed_scale")
            packed = PackedEmbedding(parts[INT8], parts[INT8_SCALE],
                                     embed_scale=getattr(module, "embed_scale", None),
                                     padding_idx=module.padding_idx)
        elif isinstance(module, torch.nn.Linear):
            packed = PackedLinear(reader.kinds[source_name], parts,
                                  module.out_features, module.in_features,
                                  fold_pre_scale=fold_pre_scale)
        else:
            raise SystemExit(f"{path} is a {type(module).__name__}, and this "
                             "build only knows how to pack Linear and Embedding")
        parent_path, _, attribute = path.rpartition(".")
        setattr(model.get_submodule(parent_path) if parent_path else model,
                attribute, packed)
        swapped.add(param_name)

    # Second pass, after every swap: a bias whose module was replaced lands on
    # the new module's buffer, which only exists once the swap has happened.
    for source_name, param_name in raw_names:
        value = reader.get_tensor(source_name, wanted[param_name].dtype).to(device)
        path, _, attribute = param_name.rpartition(".")
        owner = model.get_submodule(path) if path else model
        if isinstance(owner, (PackedLinear, PackedEmbedding)):
            # `set_module_tensor_to_device` reads the current value to find its
            # device, and a packed module declares `bias` as a None buffer.
            owner.register_buffer(attribute, value, persistent=False)
        else:
            set_module_tensor_to_device(model, param_name, device, value=value)

    assigned = swapped | {p for _, p in raw_names}
    missing = [n for n in wanted if n not in assigned and "rotary" not in n]
    if missing:
        raise SystemExit(
            f"{len(missing)} parameters were not fed from the packed file, "
            f"e.g. {missing[:5]}. The packed names do not match the model; "
            "extend LTX_RENAMES.")

    model.eval()
    print(f"resident packed model on {device}: "
          f"{resident_bytes(model) / 2**30:.3f} GiB", flush=True)
    return model


def resident_bytes(model) -> int:
    """Everything the model holds: packed buffers plus whatever stayed dense.

    Counted once. A packed module's parts are registered buffers, so walking
    `model.buffers()` after adding `packed_bytes()` counts them twice - which
    is what the first A7 run reported (13.4 GiB against an allocator peak of
    7.75 GiB). Packed buffers are collected by identity first and skipped in
    the dense pass.
    """
    seen, total = set(), 0
    for module in model.modules():
        if isinstance(module, (PackedLinear, PackedEmbedding)):
            total += module.packed_bytes()
            for name in (*_BUFFER_NAMES.values(), "bias"):
                tensor = getattr(module, name, None)
                if tensor is not None:
                    seen.add(id(tensor))
    for tensor in list(model.parameters()) + list(model.buffers()):
        if id(tensor) in seen or tensor.device.type == "meta":
            continue
        seen.add(id(tensor))
        total += tensor.numel() * tensor.element_size()
    return total


def check_gpu_kernels(device: str | None = None) -> None:
    """Refuse a wheel that has no kernels for this card, and say what to install.

    Nothing in this format needs anything unusual from a GPU - no fp8 units, no
    minimum compute capability, no bf16 tensor cores. What can be missing is
    PyTorch's own kernels: the current default wheel on PyPI is a cu130 build,
    and cu130 dropped Volta.

    Left alone, that surfaces as

        CUDA error: no kernel image is available for execution on the device

    raised from the first kernel launch - which is after an 8.46 GB load, inside
    somebody else's library, and long after `torch.cuda.is_available()` returned
    True. Checking `get_arch_list` costs nothing and moves the failure to the
    place where the fix makes sense.
    """
    if device is not None and not str(device).startswith("cuda"):
        return
    if not torch.cuda.is_available():
        return

    major, minor = torch.cuda.get_device_capability(0)
    arch = f"sm_{major}{minor}"
    compiled = torch.cuda.get_arch_list()
    # An empty list means a build that does not report them; do not guess.
    if not compiled or arch in compiled:
        return

    name = torch.cuda.get_device_name(0)
    raise SystemExit(
        f"this torch ({torch.__version__}) has no kernels for {name} ({arch}).\n"
        f"It was built for {', '.join(compiled)}, and the first CUDA op would "
        f"fail with 'no kernel image is available for execution on the device'.\n"
        f"The model is fine - it needs no custom kernels. Install a torch built "
        f"for your card, e.g. for {arch}:\n"
        f"  pip install torch --index-url https://download.pytorch.org/whl/cu128\n"
        f"or pass device='cpu' to load without touching the GPU.")


def load_packed_model(model_dir: str, packed_path: Path | str,
                      gpu_budget: str | None = None, resident: bool = False,
                      fold_pre_scale: bool = False, device: str | None = None):
    """Build the encoder from a packed file, split across GPU and CPU.

    Mirrors what `AutoModel.from_pretrained(..., device_map="auto")` does for
    the BF16 checkpoint: same conversion-mapping renames, same guard that
    every parameter was actually fed from the file. Dequantization happens
    tensor-by-tensor, so peak memory is one dequantized tensor above the
    final footprint - the 26 GB BF16 file never exists.

    Note what the default does and does not save. The weights land as BF16, so
    the *resident* footprint is the same 26 GB the BF16 checkpoint would take;
    what packing buys there is disk and the load-time peak. `gpu_budget`
    defaults to 13 GiB - what is left of a 16 GB card - and `LTX_PACKED_GPU_BUDGET`
    raises it on a larger card, where the whole encoder fits and the CPU
    offload that budget forces is pure slowdown.

    With `resident`, the quantized tensors stay in their native widths behind
    `PackedLinear` / `PackedEmbedding` and no device map is needed: the model
    costs what the file costs. `fold_pre_scale` is passed through to the
    linears and is not bit-identical - see `PackedLinear`.
    """
    import os
    import re

    check_gpu_kernels(device)

    if gpu_budget is None:
        gpu_budget = os.environ.get("LTX_PACKED_GPU_BUDGET", "13GiB")

    from accelerate import infer_auto_device_map, init_empty_weights
    from accelerate.utils import set_module_tensor_to_device
    from transformers import AutoConfig, AutoModel
    from transformers.conversion_mapping import get_checkpoint_conversion_mapping

    register_ltx_renames()
    rules = get_checkpoint_conversion_mapping("gemma4_unified") or []

    def rename(name: str) -> str:
        for rule in rules:
            sources = rule.source_patterns
            targets = rule.target_patterns
            if isinstance(sources, str):
                sources, targets = [sources], [targets]
            for source, target in zip(sources, targets, strict=True):
                new = re.sub(source, target, name)
                if new != name:
                    return new
        return name

    config = AutoConfig.from_pretrained(model_dir)
    # `from_config` does not resolve the attention implementation the way
    # `from_pretrained` does; left unset, a standalone build falls back to
    # eager and mis-applies the boolean SDPA mask (see ltx_gptq).
    config._attn_implementation = "sdpa"
    if hasattr(config, "text_config"):
        config.text_config._attn_implementation = "sdpa"
    with init_empty_weights():
        model = AutoModel.from_config(config)
    model = model.to(torch.bfloat16)

    reader = PackedCheckpoint(packed_path)
    wanted = dict(model.state_dict())
    if resident:
        return install_resident(model, reader, wanted, rename,
                                device=device, fold_pre_scale=fold_pre_scale)

    device_map = infer_auto_device_map(
        model, max_memory={0: gpu_budget, "cpu": "40GiB"},
        dtype=torch.bfloat16, no_split_module_classes=["Gemma4UnifiedTextDecoderLayer"])

    # `set_module_tensor_to_device` places tensors but installs no hooks, so a
    # split map produces a model that only fails once a forward crosses the
    # boundary - as a device mismatch deep inside a layernorm. Refuse it here
    # instead, and say what to do about it.
    placements = set(device_map.values())
    if len(placements) > 1:
        raise SystemExit(
            f"the packed model does not fit in {gpu_budget} and would be split "
            # str(): a device map mixes GPU ordinals with "cpu", and sorting
            # those against each other is a TypeError - which is how this
            # message first announced itself.
            f"across {sorted(map(str, placements))}. This loader dispatches no "
            "hooks, so a "
            "split model raises mid-forward. Raise LTX_PACKED_GPU_BUDGET, or use "
            "resident=True, which needs only what the file costs.")

    def target_device(param_name: str):
        candidate = param_name
        while candidate:
            if candidate in device_map:
                return device_map[candidate]
            candidate = candidate.rsplit(".", 1)[0] if "." in candidate else ""
        return device_map.get("", "cpu")

    assigned = set()
    for source_name in reader.keys():  # noqa: SIM118 - PackedCheckpoint, not a dict
        param_name = rename(source_name)
        if param_name not in wanted:
            continue  # aggregates and asset blobs live outside the module tree
        value = reader.get_tensor(source_name, wanted[param_name].dtype)
        set_module_tensor_to_device(model, param_name, target_device(param_name),
                                    value=value)
        assigned.add(param_name)

    missing = [name for name in wanted
               if name not in assigned and "rotary" not in name]
    if missing:
        raise SystemExit(
            f"{len(missing)} parameters were not fed from {packed_path}, "
            f"e.g. {missing[:5]}. The packed names do not match the model; "
            "extend LTX_RENAMES.")
    model.eval()
    return model


# ---------------------------------------------------------------------------
# Resident modules
#
# `load_packed_model` dequantizes to BF16, which saves the load-time peak and
# nothing else: the model that comes out is the same 26.264 GB it always was.
# These modules keep the packed widths in memory and dequantize inside
# `forward`, which is what turns 8.463 GB on disk into 8.463 GB resident.
#
# Affordable here because the encoder runs once per prompt - no KV cache, no
# autoregressive loop. Measured on this checkpoint's real shapes at 1024
# tokens, a full dequantize costs 9-12% of the matmul it feeds.
# ---------------------------------------------------------------------------


class PackedLinear(torch.nn.Module):
    """`nn.Linear` whose weight is stored in its native widths.

    `forward` rebuilds the weight with the same arithmetic `unpack_tensor` uses
    at load time, so its output is bit-identical to the dequantized model's.
    That equality is the point: without it a resident run cannot be compared
    against any figure recorded from the dequantized path.

    `fold_pre_scale` trades the equality for speed. AWQ smoothing is
    ``W' = W * s`` and the stored weight carries the closing ``/ s``, so
    ``x @ (Q/s).T == (x/s) @ Q.T`` - the division can move onto the activation
    and shrink an ``[out, in]`` elementwise pass to a ``[tokens, in]`` one. It
    changes float rounding order, so it is off by default and its difference is
    measured rather than assumed.
    """

    def __init__(self, kind: str, parts: dict, out_features: int,
                 in_features: int, bias: torch.Tensor | None = None,
                 dtype: torch.dtype = torch.bfloat16,
                 fold_pre_scale: bool = False) -> None:
        super().__init__()
        if kind not in ("nvfp4", "int8"):
            raise ValueError(f"{kind!r} is not a packed weight kind")
        self.kind = kind
        self.out_features = out_features
        self.in_features = in_features
        self.compute_dtype = dtype

        for suffix, buffer in _BUFFER_NAMES.items():
            tensor = parts.get(name_part(suffix, parts))
            self.register_buffer(buffer, tensor, persistent=False)
        self.register_buffer("bias", None if bias is None else bias.detach(),
                             persistent=False)
        self.fold_pre_scale = bool(fold_pre_scale) and self.pre_scale is not None

    def packed_bytes(self) -> int:
        """What this module actually costs, for a device map that cannot infer it."""
        total = 0
        for buffer in (*_BUFFER_NAMES.values(), "bias"):
            tensor = getattr(self, buffer, None)
            if tensor is not None:
                total += tensor.numel() * tensor.element_size()
        return total

    @property
    def weight(self) -> torch.Tensor:
        """The dequantized weight.

        A property rather than a buffer so that code reaching for
        `module.weight` - `transformers` does, in places - still works, at the
        cost of materializing it for that one call.
        """
        return self.dequantize()

    def dequantize(self, pre_scale: bool = True) -> torch.Tensor:
        pre = self.pre_scale if (pre_scale and not self.fold_pre_scale) else None
        if self.kind == "nvfp4":
            grid = decode_nvfp4(nibble_unpack(self.codes), self.group_scale,
                                self.global_scale, pre)
        else:
            grid = decode_int8(self.ints, self.int8_scale, pre)
        return grid.to(self.compute_dtype)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        if self.fold_pre_scale:
            x = x / self.pre_scale.to(x.dtype)
        return torch.nn.functional.linear(x, self.dequantize(), self.bias)

    def extra_repr(self) -> str:
        return (f"in_features={self.in_features}, out_features={self.out_features}, "
                f"kind={self.kind}, packed={self.packed_bytes() / 2**20:.1f} MiB"
                + (", folded" if self.fold_pre_scale else ""))


class PackedEmbedding(torch.nn.Module):
    """`nn.Embedding` over int8 rows with a per-row scale.

    `model.embed_tokens` is [262144, 3840] - 1.008 GB packed against 2.016 GB
    BF16, the largest single tensor in the file, and the one DmitryDB's recipe
    keeps in BF16 as a precision island. Quantizing it is most of our disk
    advantage, and half of that is given back if it dequantizes at load.

    Gathering before scaling means only the rows a prompt touches are ever
    dequantized, which is a few thousand of 262144.

    `embed_scale` is not optional in practice. Gemma's embedding is a
    `Gemma4UnifiedTextScaledWordEmbedding`, whose forward is
    ``super().forward(ids) * embed_scale`` with `embed_scale` around sqrt(3840).
    Replacing the module without carrying that factor would drop every
    embedding by ~62x and still produce plausible-looking tensors, so the caller
    must read it off the module it is replacing.
    """

    def __init__(self, ints: torch.Tensor, scale: torch.Tensor,
                 dtype: torch.dtype = torch.bfloat16,
                 embed_scale: torch.Tensor | None = None,
                 padding_idx: int | None = None) -> None:
        super().__init__()
        self.num_embeddings, self.embedding_dim = ints.shape
        self.compute_dtype = dtype
        self.padding_idx = padding_idx
        self.register_buffer("ints", ints, persistent=False)
        self.register_buffer("int8_scale", scale, persistent=False)
        self.register_buffer("embed_scale", embed_scale, persistent=False)

    def packed_bytes(self) -> int:
        return (self.ints.numel() * self.ints.element_size()
                + self.int8_scale.numel() * self.int8_scale.element_size())

    @property
    def weight(self) -> torch.Tensor:
        return decode_int8(self.ints, self.int8_scale, None).to(self.compute_dtype)

    def forward(self, index: torch.Tensor) -> torch.Tensor:
        rows = self.ints[index].float()
        scale = self.int8_scale[index].unsqueeze(-1).float()
        out = (rows * scale).to(self.compute_dtype)
        if self.embed_scale is not None:
            # Cast then multiply, in that order, because that is what
            # `Gemma4UnifiedTextScaledWordEmbedding.forward` does.
            out = out * self.embed_scale.to(self.compute_dtype)
        return out

    def extra_repr(self) -> str:
        scaled = "" if self.embed_scale is None else f", embed_scale={float(self.embed_scale):.4g}"
        return (f"{self.num_embeddings}, {self.embedding_dim}, kind=int8, "
                f"packed={self.packed_bytes() / 2**20:.1f} MiB{scaled}")


def name_part(suffix: str, parts: dict) -> str:
    """The key in `parts` carrying `suffix`, or a miss the caller tolerates.

    `parts` comes either straight from a packer (keys are bare suffixes) or
    from a checkpoint read (keys are `name + suffix`), and both are worth
    supporting so a test does not have to fabricate tensor names.
    """
    if suffix in parts:
        return suffix
    for key in parts:
        if key.endswith(suffix):
            return key
    return suffix


def packed_linear_apply(reader, name: str, x: torch.Tensor,
                        bias: torch.Tensor | None = None, rows: int = 512,
                        dtype: torch.dtype = torch.float32) -> torch.Tensor:
    """`F.linear(x, W, bias)` for a packed W, `rows` output rows at a time.

    The two LTX aggregate tables are [4096, 188160] and [2048, 188160]. Reading
    one whole and casting it to float32 costs 3.08 GB, which is why
    `apply_aggregates` only runs after the encoder has been unloaded. Chunking
    is over *output* rows, so the contraction over `in_features` is untouched
    and the result is bit-identical to decoding the table in one piece.
    """
    out_features = reader.row_count(name)
    pieces = []
    for start in range(0, out_features, rows):
        stop = min(start + rows, out_features)
        weight = reader.get_tensor_rows(name, start, stop, dtype)
        piece = None if bias is None else bias[start:stop]
        pieces.append(torch.nn.functional.linear(x, weight, piece))
        del weight
    return torch.cat(pieces, dim=-1)


def cli_info(args) -> int:
    reader = PackedCheckpoint(args.packed)
    from collections import Counter
    counts = Counter(reader.kinds.values())
    size = Path(args.packed).stat().st_size
    print(f"{args.packed}: {size / 1e9:.3f} GB, {dict(counts)}")
    print(f"negative-zero collapses at pack time: "
          f"{reader.metadata.get('negative_zero_collapses')}")
    for key in sorted(set(reader.metadata) - {"kinds"}):
        print(f"  {key}: {reader.metadata[key][:100]}")
    return 0


def cli_verify(args) -> int:
    """Compare every tensor against a reference BF16 checkpoint, streaming."""
    from safetensors import safe_open
    reader = PackedCheckpoint(args.packed)
    reference = safe_open(str(args.reference), framework="pt")
    names = set(reference.keys())
    missing = sorted(set(reader.kinds) - names)
    extra = sorted(names - set(reader.kinds))
    if missing or extra:
        raise SystemExit(f"tensor sets differ: missing {missing[:5]}, extra {extra[:5]}")
    worst = 0
    for i, name in enumerate(sorted(names)):
        want = reference.get_tensor(name)
        got = reader.get_tensor(name, want.dtype)
        if not torch.equal(got, want):
            raise SystemExit(f"{name}: dequantized values differ from reference")
        worst += int((got.signbit() != want.signbit()).sum())
        if i % 100 == 0:
            print(f"  {i}/{len(names)} verified", flush=True)
    print(f"all {len(names)} tensors value-exact; {worst} zero-sign differences")
    return 0


def cli_emit_bf16(args) -> int:
    """Materialize a plain BF16 checkpoint a stock loader can read."""
    from safetensors.torch import save_file
    reader = PackedCheckpoint(args.packed)
    tensors = {}
    for name in sorted(reader.kinds):
        tensors[name] = reader.get_tensor(name, torch.bfloat16)
    save_file(tensors, str(args.output))
    print(f"wrote {args.output} ({Path(args.output).stat().st_size / 1e9:.2f} GB)")
    return 0


def main() -> int:
    parser = argparse.ArgumentParser()
    sub = parser.add_subparsers(dest="command", required=True)
    sub.add_parser("info").add_argument("packed")
    p = sub.add_parser("verify")
    p.add_argument("packed")
    p.add_argument("--reference", required=True)
    p = sub.add_parser("emit-bf16")
    p.add_argument("packed")
    p.add_argument("--output", required=True)
    args = parser.parse_args()
    return {"info": cli_info, "verify": cli_verify,
            "emit-bf16": cli_emit_bf16}[args.command](args)


if __name__ == "__main__":
    raise SystemExit(main())