mixle.models.sparsity_2_4 module¶
2:4 structured sparsity, end to end (roadmap I4): training-time mask ramp + cuSPARSELt-format export.
Two pieces, glued by ONE borrowed primitive rather than two reimplementations:
TwoFourSparsityRamp– a schedulable training-time mask ramp. It does not reinvent 2:4 masking: at every ramp step it calls G2’smixle.models.sigma_weighted_projection. sigma_weighted_block_sparse()with the literal"2:4"pattern, which is the actual masking/value -readjustment mechanism (see that module’s docstring for the alternating-projection algorithm). This module’s own job is just the RAMP: which fraction of a weight matrix’s rows are already under the hard 2:4 constraint at a given training step, growing from 0% atstart_stepto 100% atend_step. The ramp’s step-to-fraction map is a plain, swappable callable (schedule) precisely so a future roadmap-H3 “structure-edit schedule” controller could drive it (supply a differentschedule, or mutatestart_step/end_stepon the fly) without touching this module – H3 itself is NOT built here, only the seam it would plug into.export_2_4_compressed()/decompress()– the actual cuSPARSELt-style compressed-matrix format: for every contiguous group of 4 weights along the input (last) axis that already satisfies the 2:4 constraint (exactly 2 nonzeros), store the 2 surviving VALUES plus a small INDEX recording which 2 of the 4 in-group positions they came from. This is the documented shape of NVIDIA’s semi-structured sparse storage (see e.g. the cuSPARSELt / Ampere structured-sparse-tensor-core docs andtorch.sparse.SparseSemiStructuredTensor): compressed values at half the density, plus small per-group metadata recording nonzero positions (not full-size, since onlyC(4,2) = 6patterns are possible per group). NVIDIA does not publish the exact bit-for-bit metadata layout their kernels consume (it is treated as an opaque, hardware/kernel-generation-specific detail even insidetorch.sparse.SparseSemiStructuredTensor), so the exact packing implemented here (2 bits per in-group position, 2 positions packed per byte-nibble, 2 nibbles per byte) is THIS module’s own documented, round-trip-correct encoding of the publicly documented “values + position indices” shape – not a claim of bit-exact compatibility with a specific cuSPARSELt release. Values are portable float32/float64; a real cuSPARSELt handle would additionally repack this into its internal opaque compressed-matrix object viacusparseLtSpMMACompress(or, in this torch build,torch.sparse.to_sparse_semi_structured), which requires a CUDA tensor and a cuSPARSELt-capable GPU – seecusparselt_status()and the module docstring inmixle/tests/sparsity_2_4_test.pyfor what was actually checked/measured in THIS environment (no CUDA device here – see that check’s output).
Environment note (checked, not assumed): this environment has no CUDA device (torch.cuda.is_available()
is False), so torch.backends.cusparselt reports unavailable and
torch.sparse.to_sparse_semi_structured raises (CPU tensors are not supported by that call at all, CUDA
or not). The compress/decompress round trip below is pure CPU numpy/torch and is exercised directly
(round-trip exactness + measured byte-size compression ratio are pinned by tests); no accelerated
cuSPARSELt kernel is run anywhere in this module. See cusparselt_status() for a queryable summary of
what this torch build actually offers.
- class TwoFourSparsityRamp(start_step, end_step, target_density=0.5, schedule=None)[source]
Bases:
objectSchedulable training-time 2:4 mask ramp.
start_step/end_step: training steps between which the constrained-row fraction grows from 0 to 1 (seeschedule).target_density: the density the 2:4 CONSTRAINT itself enforces once applied – structurally fixed at0.5(2 of every 4), kept as an explicit constructor argument (rather than hardcoded) so the acceptance criterion “2:4 model at stated … density” is documented at the call site and so a mismatched caller expectation fails loudly at construction time instead of silently.schedule(step, start_step, end_step) -> fraction in [0, 1]: the pluggable ramp-shape callable – THE seam a future roadmap-H3 structure-edit-schedule controller would drive (a non-linear cubic ramp, a warmup-then-hold schedule, one keyed off validation loss instead of step count, etc.); defaults to_linear_ramp(). H3 itself is not implemented here – this is deliberately just a callable/ parameter another component could supply.- fraction(step)[source]
Fraction of the weight matrix’s OUTPUT ROWS that are under the hard 2:4 constraint at
step, in[0, 1], clipped in case a customscheduleover/undershoots.
- project(weight, step, sigma=None)[source]
Apply the ramp to a weight matrix (
d_out x d_in,d_ina multiple of 4): the firstn_constrained_rows(step, d_out)rows are hard-projected onto the 2:4 pattern via G2’ssigma_weighted_block_sparse()(the real masking/value-adjustment mechanism – this is not a magnitude-only reimplementation); the remaining rows are left untouched (still fully dense at this point in the ramp). Returns a NEW array/tensor of the same type and shape asweight; does not mutateweightin place (callers doing in-place training updates copy the result back themselves, seeapply_ramp_to_linear_()).sigma: optionald_in x d_incovariance to weight the projection by (e.g. a real propagated law from G1/moment_propagation, wired the same way G2’s own acceptance test wires it). Defaults to the identity, which reduces the Sigma-weighted objective to plain Frobenius reconstruction – i.e. plain magnitude-based 2:4 value selection when no activation-covariance estimate is available, which is the common case during ordinary token-level training where no data-free law has been propagated for this weight.Rows are constrained from index 0 upward (a fixed, deterministic ordering) rather than by magnitude or another heuristic – keeps the ramp’s mask-growth trajectory the same across calls for the same
step, which is what the ramp-correctness test below checks against.
- apply_(linear, step, sigma=None)[source]
In-place convenience: project
linear.weight.datathroughproject()and copy the result back. This is the “mask-then-continue-training” step a caller runs after every optimizer step (seetrain_with_rampbelow) – gradients keep flowing through the dense parameter between projections (straight-through), and the projection is re-applied (with a re-selected pattern, per G2) every call, which is what lets already-constrained rows keep adapting their surviving VALUES as training continues, not just their support.
- class Compressed2to4(values, indices, shape)[source]
Bases:
objectA 2:4 semi-structured-sparse compressed matrix, cuSPARSELt-shaped: the 2 surviving values per group of 4 (
values, half the density of the dense matrix) plus a small per-group index recording which 2 of the 4 in-group positions they occupy (indices). See the module docstring for exactly howindicesis packed (2 bits per in-group position, 2 groups’ worth of index nibbles per byte) and why that specific bit layout is this module’s own documented encoding rather than a claim of bit-for-bit compatibility with a specific NVIDIA driver’s opaque compressed-matrix object.values: float array, shape(d_out, d_in // 4, 2)– the 2 surviving values per group, in ASCENDING in-group-position order (position ofvalues[..., 0]<= position ofvalues[..., 1]; this ordering convention is what letsindicesunambiguously address them on decompress).indices:uint8array, shape(d_out, ceil(d_in // 4 / 2))– packed 2-bits-per-position metadata, 2 groups per byte (low nibble = group2k’s two 2-bit positions, high nibble = group2k+1’s, when present).shape: the original dense(d_out, d_in)shape, needed to reconstructdecompress’s output (the last packed index byte may cover an odd group count).
- export_2_4_compressed(weight_2_4_masked)[source]
Convert an already-2:4-masked dense weight matrix into the compressed (values + indices) storage format cuSPARSELt-style semi-structured sparse GEMM kernels consume. Precondition: every contiguous group of 4 entries along the last axis already has AT MOST 2 nonzeros (checked; raises if violated – this function does not itself select the 2:4 pattern, that’s
TwoFourSparsityRamp.project()/ G2’ssigma_weighted_block_sparse, this is purely the storage-format conversion of an already- constrained matrix, matching how a real cuSPARSELt workflow separates “prune/select the pattern” from “compress for the kernel”).- Parameters:
weight_2_4_masked (Any)
- Return type:
Compressed2to4
- decompress(compressed)[source]
Exact inverse of
export_2_4_compressed(): reconstruct the dense (2:4-sparse) matrix fromvalues/indices. Round-trips EXACTLY (bit-for-bit on the surviving values, genuine zeros elsewhere) – pinned directly by the compress/decompress test.- Parameters:
compressed (Compressed2to4)
- Return type:
- cusparselt_status()[source]
Honest, queryable snapshot of what THIS torch build/environment actually offers for real cuSPARSELt-accelerated 2:4 sparse GEMM – used by the tests to decide (and clearly LABEL) whether an “inference speedup” number is a real measurement or a theoretical bound. Never raises: every field degrades to
False/Noneif the relevant torch API is missing entirely (older torch builds).