solver: add optional component-area pruning
Full-height skyline columns partition the remaining board. Add sound gcd and bounded subset-sum checks for the resulting component areas, with boundary-event and periodic benchmark schedules. Keep the rules disabled by default because their small tree reductions do not recover their measured cost. Record the rejected default and scheduling evidence so it can be revisited only with new data. Tests: Debug CTest (14 passed) Tests: ASan+UBSan CTest (14 passed) Refs: #13
This commit was merged in pull request #28.
This commit is contained in:
@@ -24,6 +24,10 @@ otherwise identical unconstrained baseline.
|
||||
The production default also applies the pruning rules described below.
|
||||
Use `--pruning valley-capacity`, `--pruning large-square`, or
|
||||
`--no-pruning` to measure each rule alone or obtain an unpruned search.
|
||||
Use `--component-pruning gcd`, `--component-pruning subset-sum`, or
|
||||
`--component-pruning none` to compare the component-area rule separately.
|
||||
Component pruning is disabled by default because the measurements below do not
|
||||
recover its cost.
|
||||
Order 9 uses the constructive odd-order path, searching order 8 and then tiling
|
||||
the enlarged border, so it is suitable for normal local benchmarking:
|
||||
|
||||
@@ -157,6 +161,62 @@ is already recovered; it was not added without evidence that the complexity
|
||||
would improve elapsed time. The simple every-node scan remains enabled by
|
||||
default for the measured public benchmark benefit.
|
||||
|
||||
## Component-area pruning
|
||||
|
||||
In a skyline, every non-full column is empty from its filled height to the top
|
||||
of the board. Adjacent non-full columns therefore belong to the same empty
|
||||
component, while a full-height column is an impassable separator. Each
|
||||
remaining square must lie wholly within one such component, so every component
|
||||
area must be the sum of a bounded subset of the remaining square areas.
|
||||
|
||||
The `gcd` mode first rejects a component area which is not divisible by the
|
||||
greatest common divisor of all remaining square areas. The `subset-sum` mode
|
||||
then computes exact reachable areas using each remaining multiplicity as a
|
||||
bound. Each component is checked against the same reachable set; this is a
|
||||
necessary condition, not a claim that independently selected subsets are
|
||||
mutually disjoint.
|
||||
|
||||
The check is triggered only after a placement reaches full board height and
|
||||
can create or extend a component boundary. This keeps the potentially more
|
||||
expensive bounded subset sum off ordinary nodes. Instrumented output records
|
||||
`component_area_checks`, `component_area_prunes`, `component_gcd_prunes`, and
|
||||
`component_subset_prunes`. The three `--component-pruning` modes allow the
|
||||
trigger cost, cheap gcd rule, and bounded subset sum to be compared directly.
|
||||
Use `--component-schedule periodic-8` to compare the event-driven boundary
|
||||
trigger with checking every eighth placement depth.
|
||||
|
||||
Measurements used the issue #13 working tree based on commit `220cec0`, Apple
|
||||
Clang 21.0.0, `-O3 -DNDEBUG`, macOS arm64, one worker, one warm-up, and seven
|
||||
measured repetitions for the boundary-trigger modes. Counter-free and counted
|
||||
runs were interleaved; the table reports counter-free medians. The existing
|
||||
valley-capacity and large-square rules remained enabled. All results passed
|
||||
independent validation and counters were stable:
|
||||
|
||||
| Order | Component rule | Median solve | Nodes | Checks | Prunes |
|
||||
| --- | --- | ---: | ---: | ---: | ---: |
|
||||
| 7 exhaustive | disabled | 0.985 s | 13,221,239 | 0 | 0 |
|
||||
| 7 exhaustive | gcd | 1.027 s | 13,220,729 | 171,088 | 2,568 |
|
||||
| 7 exhaustive | subset sum | 1.052 s | 13,189,961 | 161,706 | 69,483 |
|
||||
| 8 first solution | disabled | 0.203 s | 2,597,678 | 0 | 0 |
|
||||
| 8 first solution | gcd | 0.212 s | 2,597,548 | 23,943 | 637 |
|
||||
| 8 first solution | subset sum | 0.219 s | 2,592,212 | 23,097 | 11,858 |
|
||||
|
||||
Gcd-only checking changed fewer than 0.005% of nodes while slowing the
|
||||
counter-free solver by 4.3% for both orders. Bounded subset sum reduced nodes
|
||||
by only 0.24% for order 7 and 0.21% for order 8, while slowing them by 6.8% and
|
||||
7.5%. Neither rule is enabled by default.
|
||||
|
||||
The boundary trigger is an incremental event check: it runs only when the
|
||||
latest placement reaches full height and can change the component partition.
|
||||
For comparison, subset sum was also sampled every eighth placement depth with
|
||||
one warm-up and three measured repetitions. Periodic checking made 1,501,035
|
||||
checks for order 7 and 290,774 for order 8, versus 161,706 and 23,097 at
|
||||
boundary events. Its counter-free medians were 1.088 s and 0.227 s, 10.5% and
|
||||
11.7% slower than disabled pruning and materially worse than boundary
|
||||
triggering. The periodic schedule is retained only as an opt-in measurement
|
||||
mode; event-triggered checking is the cheaper schedule if component pruning is
|
||||
reconsidered with new evidence.
|
||||
|
||||
## D4 board symmetry
|
||||
|
||||
Every solution contains exactly one 1-by-1 square. Rotations and reflections
|
||||
|
||||
Reference in New Issue
Block a user