feat(compression): add-encoding-partition Phase 2 — per-block dispatch - #109
Merged
Conversation
Wires the per-block compress + stitch logic deferred from Phase 1 (PR #107). Compressor.apply now actually runs per-block dispatch when an encoding_partition is supplied, instead of refusing with NotImplementedError. What ships polygram/compression/compressor.py (~280 new lines) Five new private helpers for the per-block path: _partition_global_plan_into_blocks(global_plan, partition) Splits a globally-computed CompressionPlan into per-block cluster lists. Clusters whose members span more than one block are DROPPED (their features end up as singletons — cross-block merges are semantically invalid since the two blocks use different encodings). Returns (per_block_clusters, n_cross_block_dropped). _build_local_plan_for_block(block_clusters, block) Re-indexes a block's ClusterPlan members + representative + zeroed to LOCAL indices into the block's sliced W_dec (positions 0..len(block.feature_ids)-1). Cluster ids stay GLOBAL so the BlockReport's cluster_assignments and the top-level CompressionReport.plan reference the same id space. _slice_state_to_block(source_state, block) Returns a copy of source_state with W_dec/W_enc/b_enc column-sliced to block.feature_ids. b_dec is shared (invariant under feature-axis slicing; strategies don't touch it). _stitch_block_into_state(global_state, sub_rewritten, block) Writes per-block rewritten rows back into global_state at the positions named by block.feature_ids. Mutates global_state in place. _build_block_report(block, block_clusters, sub_rewritten_w_dec, sub_source_w_dec, sub_merged_norms) Builds the BlockReport for one block with per-feature cluster_assignments (LOCAL cluster ids in [0, n_clusters_in_block); -1 for features not in any cluster). v1 leaves per-block rank_ratio/post_A/forge_mse as None — those diagnostic floats are a separate enhancement. _apply_partitioned(source_state, global_plan, partition, ...) The top-level driver. Calls the helpers above per block, stitches the rewritten state, builds block_reports. Returns (rewritten_state, merged_norms, block_reports, n_cross_block_dropped). Compressor.apply integration: - Replaces the Phase 1 NotImplementedError refusal with the actual per-block dispatch path. - Coverage validation runs immediately (before any I/O) so an under- or over-specified partition fails fast. - The top-level CompressionReport's plan is rebuilt to drop cross-block clusters; the apply()'s downstream rank_ratio / post_A / scale_compression_ratio diagnostics then reflect the cross-block-dropped clusters, not the original global ones. - CompressionReport.blocks is populated with the BlockReport tuple. - Defensive: when plan.feature_ids is empty (all clusters cross-block dropped), the downstream rebuild seeds from the lowest-fid features so the rebuilt Dictionary still surfaces as a debugging aid. BlockReport.n_features_kept semantic aligned with top-level CompressionReport.n_features_kept: count of cluster representatives (= n_clusters), not all surviving features in the block. Singletons stay singletons; only multi-feature clusters contribute to the kept count. Tests (9 new cases, 1 Phase 1 case updated) tests/compression/test_encoding_partition_phase2.py (new) - test_single_block_partition_runs_strategy_correctly: 1-block partition covering all features matches the top-level CompressionReport's counts. - test_two_block_partition_stitches_correctly: 2 blocks with intra-block clusters; per-block + top-level counts agree. - test_two_block_partition_zeroes_correct_rows_in_output: verifies the stitched output W_dec has the right rows zeroed (uses explicit n_fires for deterministic rep selection). - test_cross_block_clusters_are_dropped: confirmed pair bridging two blocks is dropped; both features survive as singletons. - test_coverage_validation_fires_on_incomplete_partition: PartitionCoverageError fires at apply() time before I/O. - test_coverage_validation_fires_on_overlapping_partition: overlap raises with the duplicate id named. - test_block_report_cluster_assignments_local_to_block: cluster_assignments uses local indices 0..n_clusters_in_block-1 with -1 sentinels for features not in any cluster. - test_full_compression_report_with_blocks_round_trips: v3 report with blocks round-trips through to_json / from_json. - test_no_partition_uses_single_encoding_path: regression guard — when encoding_partition=None the historical path runs and CompressionReport.blocks is None. tests/compression/test_add_encoding_partition.py: test_compressor_apply_refuses_encoding_partition (Phase 1) replaced with test_compressor_apply_runs_partition_path_in_phase_2 — pins the Phase 1 → Phase 2 transition; the Phase 1 NotImplementedError refusal is gone. Tests: 1070 → 1079 (+9). Full suite green. Ruff clean. Phase 2 v1 caveats (documented enhancements, not blockers) - Per-block rank_ratio / post_A / forge_mse: deferred to a separate enhancement. Each block's diagnostic floats currently serialise as None in the BlockReport. The top-level CompressionReport's rank_ratio / post_A still compute (across the dropped-cross-block plan). - merged_norms aggregation: the per-block sub_merged_norms maps use the GLOBAL cluster_id (preserved through the local-plan construction in _build_local_plan_for_block), so the top-level merged_norms dict is a clean union of per-block dicts. No namespace collisions. - merge strategy: works the same as zero — the strategy is called per-block on each sub-state, so merge_mode is honored. Tests above cover only zero strategy explicitly; merge support is implicit through the existing dispatch_strategy call. Coordination After this lands, polygram tags v0.14.0 → sae-forge can begin add-block-structured-sae Phase 2 (per-block dispatch in auto_materialise.py, sweep.py, forge.py) against the now-functional partition surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
jascal
added a commit
that referenced
this pull request
May 21, 2026
Tags the polygram release that ships add-encoding-partition Phase 2 (#109): the actual per-block dispatch + stitching logic that Phase 1 (v0.13.0) deferred behind a NotImplementedError. What's new in 0.14.0 Compressor.apply now runs per-block compression when encoding_partition is set: - Per-block W_dec/W_enc/b_enc slicing - Cross-block clusters dropped (semantically invalid) - CompressionReport.blocks populated end-to-end - Coverage validation fires before any I/O After this tag publishes, sae-forge can begin add-block-structured-sae Phase 2 — the --encoding-partition CLI flag, AutoMaterialiseSpec.encoding_partition field, and per-row partition_label in ParetoFrontierRow — against a now-functional polygram partition surface. Phase 2 v1 caveats (already documented in PR #109's body and the CHANGELOG entry): per-block rank_ratio/post_A/forge_mse deferred to a separate enhancement; merge strategy works implicitly through the existing dispatch_strategy call. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
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
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wires the per-block compress + stitch logic that Phase 1 (#107) deferred.
Compressor.applynow actually runs per-block dispatch when anencoding_partitionis supplied, instead of refusing withNotImplementedError.After this lands + polygram tags v0.14.0, sae-forge's
add-block-structured-saePhase 2 (per-block dispatch inauto_materialise.py/sweep.py/forge.py) can begin against the now-functional partition surface.What ships (~280 new lines)
Five new private helpers in
polygram/compression/compressor.py:_partition_global_plan_into_blocks_build_local_plan_for_block_slice_state_to_block_stitch_block_into_state_build_block_report_apply_partitionedCompressor.applyintegration:CompressionReport.blockspopulated.BlockReport.n_features_keptsemantic aligned with top-levelCompressionReport.n_features_kept: count of cluster representatives (= n_clusters), NOT total survivors.Tests (9 new + 1 Phase 1 update)
1070 → 1079 tests passing on full suite. Ruff clean.
Phase 2 v1 caveats (documented enhancements, not blockers)
None. Top-level still computes (across the post-drop plan).Coordination
After polygram tags v0.14.0, sae-forge can:
polygram>=0.14.0.add-block-structured-saePhase 2 — the--encoding-partitionCLI flag +AutoMaterialiseSpec.encoding_partitionfield + per-rowpartition_labelinParetoFrontierRow. The downstream change's spec is already filed and locked.Test plan
🤖 Generated with Claude Code