FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: Bitpack-on-quantize mode for GPTQ and DBF by k-arima-3150 · Pull Request #43 · FujitsuResearch/OneCompression · GitHub

feat: Bitpack-on-quantize mode for GPTQ and DBF - #43

Merged
aki916f merged 53 commits into
FujitsuResearch:develop/v1-3-0from
computermind-corp:feature/bitpack_mode
Jul 30, 2026
Merged

feat: Bitpack-on-quantize mode for GPTQ and DBF#43
aki916f merged 53 commits into
FujitsuResearch:develop/v1-3-0from
computermind-corp:feature/bitpack_mode

Conversation

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a bitpack_on_quantize mode that keeps quantization results in bitpacked form from the moment each layer is quantized, for both GPTQ and DBF.

Motivation. Until now, quantization results held unpacked tensors for the whole run — int32 qweight / qzeros for GPTQ, ±1 float16 dbf_A / dbf_B for DBF — and packing happened only later, when an inference layer was created or the model was saved. For large models, the accumulated unpacked results can account for a substantial share of the memory held during quantization. Packing them right after each layer is quantized reduces storage for qweight / qzeros by roughly 8x at 4-bit and for the DBF binary factors by roughly 16x, and lets the large unpacked tensors be freed early.

What changes. bitpack_on_quantize is added to the base Quantizer (default False) and enabled by default for GPTQ and DBF. AutoBitQuantizer propagates the setting to existing GPTQ and DBF child candidates before child validation and passes it to DBF fallback quantizers created by inject_dbf(). JointQ deliberately keeps its internal GPTQ initialization unpacked to preserve its existing optimization path.

Compatibility. Supported downstream paths include compute_dequantized_weight(), inference-layer construction (GPTQLinear and DoubleBinaryLinear), the covered QEP / LPCD / chunked-calibration paths, save/load round-trips, and DBF vLLM inference. Equivalence tests verify that packed storage reconstructs the same dequantized weights as unpacked storage for fixed GPTQ and DBF quantization results.

Breaking changes.

  • GPTQ quantize-time bitpacking is enabled by default and supports resolved bit-widths {2, 3, 4, 8} only. Other widths in 1..15 require bitpack_on_quantize=False; creating or saving unpacked inference weights also requires pack_weights=False.
  • GPTQ bitpacking inherits the packer's alignment requirements: both input and output dimensions must be divisible by 16, 8, or 4 for 2-, 4-, or 8-bit packing respectively, and by 32 for 3-bit packing. The requirement itself is not new — it already applied when packing at inference-layer creation or save time — but with the mode enabled by default, unaligned shapes now surface during quantization instead of later.
  • GPTQ bit-width validation is unified to 1..15 across wbits, mlp_wbits, module_wbits, and saved quantization_config loading.
  • pack_weights=True is now validated when creating a GPTQ inference layer: packer-unsupported widths raise instead of silently falling back to unpacked storage.
  • GPTQLinear.wbits handling is stricter in direct and saved-state construction: integer-valued floats are normalized to int, while bool, non-integral or non-finite floats, and other types raise ValueError. Separately, saved per-layer quantization_bits[].bits values must be integers; floats are rejected instead of truncated.
  • DBF has no such restriction: its factors are always ±1 and the packer handles arbitrary shapes through padding.

Bug fix. DoubleBinaryLinear now exposes in_features / out_features, fixing the AttributeError raised by register_online_hadamard_hooks() -> get_hadK() when re-registering Hadamard hooks on saved, DBF-quantized, rotation-preprocessed models.

Documentation, a new DBF + vLLM inference example, and unit / equivalence / runner smoke / vLLM e2e tests are included.

Changes

GPTQ

New Feature / Breaking Changes: GPTQ bitpack-on-quantize mode

  • Added bitpack_on_quantize to the base Quantizer (default False) and enabled it by default for GPTQ; for packer-supported bit widths, qweight and qzeros are stored in AutoGPTQ-compatible packed format immediately after each layer is quantized (onecomp/quantizer/_quantizer.py, onecomp/quantizer/gptq/_gptq.py)
  • Extended GPTQResult with packed-state metadata (qweight_is_packed, qzeros_is_packed, qweight_original_shape) and updated compute_dequantized_weight() so packed and unpacked results reconstruct the same dequantized weights across grouped / per-channel, symmetric / asymmetric, and act-order paths (onecomp/quantizer/gptq/_gptq.py)
  • Updated GPTQLinear to consume pre-packed GPTQResult tensors without repacking, unpack them when pack_weights=False, and handle the GPTQ v1 zero-point offset consistently for packed results and inference (onecomp/quantizer/gptq/gptq_layer.py)

Validation / compatibility tweaks

  • Limited GPTQ bit-width validation to 1..15 across wbits, mlp_wbits, module_wbits, and saved quantization_config loading (onecomp/quantizer/gptq/_gptq.py, onecomp/quantizer/gptq/config.py)
  • Restricted immediate GPTQ bitpacking to packer-supported widths {2, 3, 4, 8}; other valid GPTQ widths can still be used with bitpack_on_quantize=False (onecomp/quantizer/gptq/_gptq.py)
  • Added pack_weights=True validation during GPTQ inference layer creation, so packer-unsupported widths now fail clearly instead of silently falling back to unpacked storage (onecomp/quantizer/gptq/_gptq.py)
  • Added bitpack_on_quantize to AutoBitQuantizer and propagated it to GPTQ child quantizers before child validation, so unsupported packed GPTQ candidates fail with a clear error (onecomp/quantizer/autobit/_autobit.py)
  • Preserved JointQ's existing optimization path by keeping its internal GPTQ initialization on unpacked qweight / qzeros (onecomp/quantizer/jointq/_jointq.py)
  • Normalized GPTQLinear.wbits in direct and saved-state construction: integer-valued floats are converted to built-in int, while bool, non-integral or non-finite floats, and other types raise ValueError. is_packable_wbits() now checks membership without truncation (onecomp/quantizer/gptq/gptq_layer.py)
  • Updated base-model export to convert wbits to int only after confirming it is packable, so non-integral float widths remain unpacked (onecomp/runner.py)
  • Fixed saved per-layer quantization_bits[].bits validation to reject all floats instead of truncating them before the strict int check (onecomp/quantizer/gptq/config.py)

Documentation

  • Documented the quantize-time versus save-time packing constraints for GPTQ's bitpack_on_quantize (docs/algorithms/gptq.md).
  • Documented AutoBit's child-setting override and fused-group constraints for GPTQ candidates, and JointQ's forced unpacked GPTQ initial solution (docs/algorithms/autobit.md, docs/algorithms/jointq.md).

Tests

  • Added tests/onecomp/quantizer/gptq/test_gptq_bitpack.py for packed result metadata, dequantization, GPTQLinear.from_quantization_result() inference, unsupported bit-width errors, and packed-result shape checks.
  • Added tests/onecomp/quantizer/gptq/test_gptq_bitpack_equivalence.py for packed-vs-unpacked equivalence across supported bit-widths, grouping, symmetry, and act-order combinations.
  • Updated tests/onecomp/quantizer/gptq/test_gptq.py for the bitpack_on_quantize flag, unpacked-result compatibility, and the shared 1..15 GPTQ bit-width validation limit.
  • Updated tests/onecomp/quantizer/autobit/test_fused_group_validation.py for AutoBit-to-GPTQ bitpack_on_quantize propagation and unsupported packed GPTQ candidate validation.
  • Updated tests/onecomp/quantizer/autobit/test_autobit.py so existing AutoBit tests that exercise unpacked GPTQ candidates pass bitpack_on_quantize=False explicitly.
  • Added regression tests for wbits normalization, strict saved-config validation, and export behavior for integral and non-integral float widths (tests/onecomp/quantizer/gptq/test_gptq_layer_pack.py, tests/onecomp/quantizer/gptq/test_gptq.py, tests/onecomp/runner/test_lora_save_load_roundtrip.py)

DBF

New Feature / Breaking Changes: DBF bitpack-on-quantize mode

  • Added bitpack_on_quantize support to DBF (enabled by default), so the binary factors dbf_A / dbf_B are packed into uint8 immediately after each layer is quantized instead of being kept as unpacked +/-1 float16 matrices (onecomp/quantizer/dbf/_dbf.py)
  • Extended DBFResult with packed-state metadata (dbf_A_is_packed, dbf_B_is_packed, dbf_A_original_shape, dbf_B_original_shape) and a get_unpacked_binary_factors() helper, and updated compute_dequantized_weight() so packed and unpacked results reconstruct bit-identical dequantized weights (onecomp/quantizer/dbf/_dbf.py)
  • Updated DoubleBinaryLinear to consume pre-packed DBFResult factors without repacking (registering them directly into bp1 / bp3), still pack unpacked inputs, and preserve the existing from_saved_state() behavior (onecomp/quantizer/dbf/dbf_layer.py)

Validation / compatibility tweaks

  • Added bool validation for bitpack_on_quantize in DBF.validate_params() (onecomp/quantizer/dbf/_dbf.py)
  • Extended the bitpack_on_quantize propagation in AutoBitQuantizer to DBF child candidates and DBF fallback quantizers created by inject_dbf() (onecomp/quantizer/autobit/_autobit.py, onecomp/quantizer/autobit/dbf_fallback.py)
  • DBF bitpacking has no bit-width restriction because the binary factors are always ±1 (1 bit), and arbitrary tensor shapes are supported through padding

Bug Fix

  • Exposed in_features / out_features on DoubleBinaryLinear (derived from the original unpacked binary-factor shapes _bp1_shape / _bp3_shape, and also set in from_saved_state()). register_online_hadamard_hooks() -> get_hadK() introspects module.in_features, but DoubleBinaryLinear previously stored only _bp1_shape / _bp3_shape, raising AttributeError when re-registering Hadamard hooks on saved DBF-quantized rotation-preprocessed models (onecomp/quantizer/dbf/dbf_layer.py)

Examples

  • Added example/vllm_inference/example_dbf_vllm_inference.py, which quantizes a model with DBF, saves it, and runs vLLM inference through the DBF plugin.

Documentation

  • Documented DBF quantize-time bitpacking and AutoBit propagation to child and generated DBF quantizers (docs/algorithms/dbf.md, docs/algorithms/autobit.md).

Tests

  • Added tests/onecomp/quantizer/dbf/test_dbf_bitpack.py for packed result metadata, dequantization, DoubleBinaryLinear.from_quantization_result() inference, and downstream-consumer handling of packed results.
  • Added tests/onecomp/quantizer/dbf/test_dbf_bitpack_equivalence.py for packed-vs-unpacked equivalence of compute_dequantized_weight() and the built inference layers.
  • Added tests/onecomp/quantizer/dbf/test_dbf_layer_pack.py for unpacked-input packing, packed-input repack avoidance, and from_saved_state() forward.
  • Added DBF bitpack runner smoke tests sharing tests/onecomp/quantizer/dbf/dbf_bitpack_runner_helpers.py: QEP (tests/onecomp/test_qep_dbf_bitpack_smoke.py), LPCD (tests/onecomp/lpcd/test_lpcd_dbf_bitpack_runner.py), and chunked-calibration calc_quant_error (tests/onecomp/test_dbf_bitpack_chunked_calc_error.py) all accept packed DBF results.
  • Added DBF vLLM plugin tests: config parsing/dispatch (tests/vllm_plugins/dbf/test_dbf_config.py) and quantize -> save -> vLLM generation e2e (tests/vllm_plugins/dbf/test_dbf_e2e.py).
  • Added DBF quantized/dequantized save/load round-trip cases to the rotation + quantization pipeline tests (tests/onecomp/pre_process/test_save_load_pipeline_tinyllama.py, tests/onecomp/pre_process/test_save_load_pipeline_qwen3.py).
  • Updated tests/onecomp/quantizer/dbf/test_dbf.py to be packed/unpacked agnostic and tests/onecomp/quantizer/autobit/test_fused_group_validation.py for AutoBit-to-DBF bitpack_on_quantize propagation.

cm-ysmz and others added 30 commits June 3, 2026 15:18
…itpack_mode_merge

Resolved conflicts in:
- onecomp/quantizer/gptq/_gptq.py
- tests/onecomp/quantizer/gptq/test_gptq.py
Add a DBF-specific bitpack_on_quantize flag (default True) to the DBF
quantizer dataclass and validate it as a bool in validate_params().
DBF packs arbitrary shapes via padding, so no bit-width/shape constraint
is imposed; only the bool type is checked.
Add packed-state metadata (dbf_A_is_packed, dbf_B_is_packed,
dbf_A_original_shape, dbf_B_original_shape) and a
get_unpacked_binary_factors() helper to DBFResult so callers can obtain
the unpacked +/-1 factors regardless of storage. The helper returns
float16 for both packed and unpacked storage so the representation
difference never leaks to callers. Add shape-aware unpack_binary_matrix()
(and pack_binary_matrix alias) to dbf_layer so the pack/unpack
shape-restore logic lives in one place. Old results without the metadata
fields are treated as unpacked via getattr defaults.
In DBF.quantize_layer(), when bitpack_on_quantize is enabled and DBF
produced both binary factors, pack dbf_A / dbf_B into uint8 CPU tensors
immediately after run_dbf() and record the packed-state metadata on the
DBFResult. The unpacked references are dropped early to reduce RAM held
during quantization. On DBF failure / is_dbf_quantized=False the factors
are left untouched, preserving the existing dequant error behavior.
compute_dequantized_weight() now recovers the +/-1 factors via
get_unpacked_binary_factors(), so it returns the same weight whether
dbf_A / dbf_B are stored packed or unpacked (pack/unpack is
value-preserving). The reconstruction formula is unchanged. Route the
existing in-layer unpack call sites (BitLinearPacked.forward and
DoubleBinaryLinear._unpack_bp) through the shared unpack_binary_matrix
helper to avoid duplicating the shape-restore logic.
DoubleBinaryLinear.__init__() / from_quantization_result() now accept
already-packed dbf_A / dbf_B (with their original shapes) and register
them straight into bp1 / bp3 without an unpack/re-pack round-trip;
unpacked inputs are packed as before. GemLite initialization builds a
short-lived unpacked copy for packed inputs only (not retained). The
saved checkpoint format (bp1 / bp3) and from_saved_state() are
unchanged.
Update the existing DBF quantizer test assertions that assumed unpacked
float16 dbf_A / dbf_B: check_quantize_layer branches on the packed flags
(uint8/1D when packed, float16/2D otherwise) and reconstructs the weight
via get_unpacked_binary_factors(); check_equal_results and the
apply-to-module helper compare/store factors through the same helper so
they hold for both representations. No changes were needed in the
blockwise post-processing optimizers or the vLLM DBF plugin, which work
on the unchanged bp1 / bp3 packed buffers.
…allback

Add a bitpack_on_quantize flag (default True) to AutoBitQuantizer and
propagate it to candidate quantizers that support on-quantize bitpacking:
validate_params() syncs the flag onto GPTQ/DBF child candidates (via
_sync_child_bitpack_on_quantize) before their own validation,
_assign_all_dbf() forwards it to the all-DBF quantizer, and the
inject_dbf() fallback path forwards it to every DBF it creates. A
non-bool flag raises a clear ValueError.
Add DBF quantizer tests covering both representations: default flag is
True; packing stores uint8 1D factors with shape metadata; disabling
keeps unpacked float16; packed and unpacked dequantized weights are
bit-identical (both from quantize_layer and hand-built results);
pack_binary -> unpack_binary_matrix is bit-exact across shapes; missing
original shape raises; from_quantization_result registers bp1/bp3 with
no re-pack and forward matches the dequantized linear; and a non-bool
flag raises. Add AutoBit tests that the flag is synced onto DBF
candidates and forwarded to inject_dbf fallbacks (1-bit GPTQ on a
64-wide layer to stay under the DBF threshold).
QEP, LPCD, chunked / multi-GPU quantization, and the cumulative-error
analyzer all consume DBF results only via
compute_dequantized_weight().to(device).to(dtype) and never touch
dbf_A / dbf_B directly, so no functional change is needed for the
on-quantize bitpack. Add a regression smoke test exercising that exact
consumption path (analyzer._update_weights) with a packed DBFResult,
confirming it succeeds and yields weights identical to the unpacked
representation (no packed state leaks to callers).
- pre_process pipeline: add DBF quantized/dequantized save/load round-trip
  cases for TinyLlama and Qwen3 (5 cases: GPTQ q/dq, DBF q/dq, RTN dq)
- tests/vllm_plugins/dbf/test_dbf_e2e.py: DBF quantize -> save -> config
  verification -> vLLM generation smoke (slow, CUDA/vLLM gated)
- tests/vllm_plugins/dbf/test_dbf_config.py: DBF plugin config parse/dispatch
  smoke (DbfConfig)
Add DBF packed-result smoke coverage for QEP, LPCD, and chunked
calibration calc_quant_error paths. The tests verify that each Runner
path produces packed dbf_A / dbf_B results and consumes them through
compute_dequantized_weight() without leaking storage details to callers.
register_online_hadamard_hooks -> get_hadK introspects module.in_features,
but DoubleBinaryLinear only stored _bp1_shape/_bp3_shape, raising
AttributeError when re-registering Hadamard hooks on saved DBF-quantized
rotated models. Expose in_features/out_features like nn.Linear (derived from
the original unpacked shapes; also set in from_saved_state).
Quantize a layer with bitpack_on_quantize=False vs True under identical seeds
and assert the dequantized weights match bit-exactly
(compute_dequantized_weight -> torch.equal). The corresponding DBF equivalence
test is in tests/onecomp/quantizer/dbf/test_dbf_bitpack_equivalence.py.
S4Y-K and others added 23 commits July 2, 2026 10:29
Parallels example_gptq_vllm_inference.py for the DBF path: quantize TinyLlama with DBF, save, then load and generate with vLLM.
Add a bitpack_on_quantize row to the parameters table in
docs/algorithms/dbf.md (DBF) and docs/algorithms/autobit.md (propagation to
candidate / injected-DBF quantizers).
…smz/bitpack_mode

Resolved conflicts in:
- CHANGELOG.md
- Document the breaking change in CHANGELOG
- Add a comment explaining qzeros reshape after pack/unpack
… feature/ysmz/bitpack_mode

Resolve conflicts in GPTQ/DBF bitpack-on-quantize integration:
- Unify AutoBit propagation for GPTQ and DBF child quantizers and auto-created DBF fallbacks
- Preserve GPTQ packable-width validation and DBF unrestricted 1-bit packing behavior
Equivalence is already covered by tests, and these examples do not
demonstrate user-facing usage.
- document GPTQ quantize-time and save-time packing constraints
- clarify AutoBit propagation, overrides, and fused-group restrictions
- document DBF and JointQ bitpacking behavior
- record these doc updates in the DBF and GPTQ bitpack changelog sections
…smz/bitpack_mode

Resolve the CHANGELOG.md conflict and fix a silent DBF auto-merge breakage:
- Preserve both bitpack-mode and upstream v1.3.0 changelog entries.
- Drop upstream's premature in_features/out_features assignment in
  DoubleBinaryLinear.__init__, which indexed 1-D packed uint8 factors.
- Keep the shape-derived assignment after packed/unpacked factor shapes are
  resolved, preserving the attributes required by rotation support.
- Remove references to the deleted DBF and GPTQ equivalence examples
- Add the missing Qwen3.6 vLLM example to README and the inference guide
aki916f merged commit 65a3c75 into FujitsuResearch:develop/v1-3-0 Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants


Back | FazBrowse Home | New Git URL