Performance architecture: measure, then optimise - #19
Merged
Conversation
Times truth-table construction, prime generation, chart construction and cover solving separately, across cases, conditions, sufficient share and remainder share. Each sweep is independently seeded so --max-width cannot silently change the tables below it. build_chart accepts pre-computed primes so the harness does not count prime generation twice.
Represents cubes as (mask, value) integer pairs rather than tuples of optional bits. Same algorithm, 141x faster at eight conditions (1.41s to 0.010s) and 83x end to end. Implicant.combine is no longer used internally but remains public API, so it gains a direct test.
minimize checks the chart shape once the primes are known but before the exponential cover search begins, so a slow run says so rather than appearing to hang. The run still completes and the answer is still exact. The prime count is the trigger, with thresholds taken from measurement: 53 primes solve in 0.0024s, 78 in 0.60s. Disable with complexity_guard=False.
Replaces the estimated complexity table with benchmark output. The ten-condition parsimonious solution now takes 0.45s rather than the ~36s the old table quoted.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
Benchmarks each phase of the pipeline before changing any of it, optimises
only what the measurements showed dominated, and warns callers before the
exponential phase rather than after.
Measuring first
benchmarks/profile_phases.pytimes truth-table construction, primegeneration, chart construction and cover solving separately, across four
dimensions that do not cost the same: cases, conditions, sufficient share
and remainder share. Each sweep is independently seeded, so
--max-widthcannot silently shift the tables below it.
Two phases dominate in two regimes: remainder-heavy problems (the
parsimonious case) by prime generation, dense on-sets by the cover search.
Truth-table construction barely moves with the case count — 25x the cases
costs under 2x the time.
The harness also exposed a measurement bug in itself: it was paying for
prime generation twice, because
build_chartregenerated what had justbeen generated.
build_chartnow accepts pre-computed primes.Optimising what dominated
Prime generation was the bottleneck, and it was spending its time
allocating tuples of optional bits. Representing cubes as
(mask, value)integer pairs instead makes it 141x faster at eight conditions
(1.4135s to 0.0100s), and 83x end to end (2.9913s to 0.0360s).
The algorithm is unchanged — this is a representation change, not a
different method. Every exactness, chart, R-parity, property and
multi-value test passes unchanged, which is what makes it safe to keep.
Implicant.combineis no longer called internally as a result. It stays aspublic API and gains a direct test rather than being quietly deleted.
Warning before the cliff
Exact minimisation is worst-case exponential and no heuristic is
substituted when a problem gets hard — a silently approximate answer is
worse than a slow one. What is negotiable is being told in advance.
Once the primes are known the chart shape is known, so
minimizenowraises
MinimizationComplexityWarningbefore entering the exponentialphase. The run still completes and the answer is still exact. Disable with
complexity_guard=False.The trigger is the prime count, not the condition count, with thresholds
read off the measurements: 53 primes solve in 0.0024s, 78 in 0.60s. The
integration tests lower the threshold rather than building a genuinely
explosive chart, since such a chart is by construction slow to solve.
Documentation
The complexity table in the minimisation guide quoted estimates, including
~36s for a ten-condition parsimonious solution. It now quotes benchmark
output, and that figure is 0.45s.
Verification
580 tests pass, 100% coverage, mypy strict and ruff clean, docs build
--strict.Summary by cubic
Measured each pipeline phase, made prime generation drastically faster, and added an early complexity warning so long runs don’t surprise users. Also avoided double work by letting
build_chartaccept precomputed primes and updated the docs with measured timings.New Features
minimize(..., complexity_guard=True)emitsMinimizationComplexityWarningbefore the exponential cover search when prime counts are high; silence viawarnings.simplefilteror disable withcomplexity_guard=False. Exported atsetqcaandsetqca.minimize.benchmarks/profile_phases.pymeasures truth-table, primes, chart, and cover across cases, conditions, sufficient share, and remainders (independently seeded).build_chart(..., primes=...)accepts precomputed primes to avoid regenerating them.Refactors
(mask, value)integer bitmasks instead of tuples, making it 141x faster at 8 conditions (1.41s → 0.010s) and 83x end-to-end (2.99s → 0.036s). Algorithm unchanged;Implicant.combineremains public and is directly tested.Written for commit c1d2159. Summary will update on new commits.