feat(optimize): optimal mode segmentation (ISO/IEC 18004 Annex J)#148
Open
gerchowl wants to merge 3 commits into
Open
feat(optimize): optimal mode segmentation (ISO/IEC 18004 Annex J)#148gerchowl wants to merge 3 commits into
gerchowl wants to merge 3 commits into
Conversation
The greedy optimizer can split a mixed-mode payload into segments whose combined mode/character-count header overhead exceeds the symbol capacity, even though encoding the whole payload as a single segment fits. For low-capacity Micro QR symbols this caused valid payloads to be falsely rejected with DataTooLong (pinned version) or silently encoded into a larger symbol than necessary (auto-version path). Add optimize::optimize / optimize_segments, which run the existing greedy merge and then return the single-mode baseline (one segment in the lowest common mode) whenever it is smaller. The result is never worse than greedy and never worse than the trivial single-mode encoding, so it cannot regress any currently-working encoding. Route all encode paths (push_optimal_data, encode_auto, encode_auto_micro, encode_auto_rect_micro) through it. For example "9BA3935DM3TBE4" (14 alphanumeric characters) now encodes as a Micro QR M3-L symbol (83 <= 84 data bits) instead of being rejected. Add regression tests plus a data-driven capacity-boundary conformance test across Micro, Normal, and rMQR versions.
…x J) Replace the greedy + single-mode-clamp body of optimize_segments with a dynamic program over parser run boundaries that finds the globally optimal mode segmentation. dp[b] is the minimum encoded bits for the first b runs; each transition merges a contiguous run span into one segment using the lowest common mode, costed by the exact Segment::encoded_len. Optimal segment boundaries are a subset of run boundaries, so this is exact-optimal while reusing the existing per-segment bit accounting. The public greedy Optimizer / Parser::optimize API is unchanged; only the internal segmentation used by the encode paths is upgraded. The result is never worse than greedy and never worse than the single-mode encoding, both of which are candidate segmentations the DP dominates. Add a brute-force reference test that enumerates all run-boundary segmentations and asserts the DP matches the exhaustive optimum across mixed-mode inputs and multiple versions, plus cases where the DP strictly beats both greedy and single-mode. Runs in O(k^2) time / O(k) space, k = parsed run count.
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.
Description
Implements globally optimal mode segmentation (ISO/IEC 18004 Annex J) as a
dynamic program over parser run boundaries, replacing the greedy +
single-mode-clamp heuristic inside
optimize_segments.dp[b]is the minimumencoded bits for the first
bparsed runs; each transition merges a contiguousrun span
[a, b)into one segment using the lowest common mode, costed by theexact
Segment::encoded_len. Optimal segment boundaries are always a subset ofrun boundaries (splitting a maximal same-class run only adds header overhead),
so the search is exact-optimal while reusing the existing per-segment bit
accounting. The public greedy
Optimizer/Parser::optimizeAPI is unchanged.Runs in
O(k^2)time /O(k)space, wherekis the parsed run count.Closes #146
Additional context
Note
Stacked on #147 (the minimal clamp fix). Until that merges, this PR's diff
also includes the clamp commit; I'll rebase once #147 lands — or this can be
merged standalone, in which case it supersedes #147 (close that one). Happy to
reshape into whichever you prefer.
If an O(n) optimizer is preferred over
O(k^2), the alternative is aper-character Nayuki-style DP (
O(n * modes)); I chose the run-boundary DPbecause it reuses the exact
encoded_lencost and avoids re-deriving thegrouped (
div_ceil) bit accounting. Happy to switch if you'd rather.Validation:
asserting the DP equals the exhaustive optimum across mixed-mode inputs and
multiple versions, plus cases where the DP strictly beats both greedy and the
single-mode encoding.
benches/optimize.rsscaling benchmark: confirmsO(k^2)on adversarialalternating input and sub-microsecond cost on realistic few-run input.
alphanumeric IDs via rqrr (full QR) and 500+ via rxing (Micro QR M3), all
byte-exact.
cargo +nightly fmt, clippy, and the library test suite all pass.Checklist