solver: prune unplaceable large squares
A skyline may retain enough total empty area while no longer containing a box for its largest remaining square. Scan for the required consecutive low columns and reject such monotonic dead states. Keep each pruning combination independently measurable and record the small public-path gain, the direct-order-9 regression, and the rejected periodic schedule. Tests: Debug CTest (13 passed) Tests: ASan+UBSan CTest (13 passed) Refs: #15
This commit was merged in pull request #27.
This commit is contained in:
@@ -369,6 +369,8 @@ namespace {
|
||||
failures += expect(counters.prune_checks >= counters.prune_hits &&
|
||||
counters.valley_capacity_checks >=
|
||||
counters.valley_capacity_prunes &&
|
||||
counters.large_square_checks >=
|
||||
counters.large_square_prunes &&
|
||||
counters.generated_tasks == 0 &&
|
||||
counters.completed_tasks == 0,
|
||||
"search counters are inconsistent");
|
||||
@@ -582,6 +584,71 @@ namespace {
|
||||
return failures;
|
||||
}
|
||||
|
||||
auto test_large_square_pruning() -> int {
|
||||
int failures = 0;
|
||||
auto const fragmented =
|
||||
std::vector<std::uint64_t>{0, 5, 0, 5, 0, 5};
|
||||
failures += expect(
|
||||
!skyline_has_empty_square(fragmented, 2),
|
||||
"large-square check accepted a fragmented state without a 2x2 box");
|
||||
failures += expect(
|
||||
skyline_has_empty_square(
|
||||
std::vector<std::uint64_t>{0, 0, 5, 5, 5, 5}, 2),
|
||||
"large-square check rejected an available 2x2 box");
|
||||
|
||||
Avail available(4);
|
||||
available[1] = 1;
|
||||
available[2] = 1;
|
||||
failures += expect(
|
||||
!remaining_large_square_fits(fragmented, available),
|
||||
"remaining-square check ignored the impossible largest square");
|
||||
available[2] = 0;
|
||||
failures += expect(
|
||||
remaining_large_square_fits(fragmented, available),
|
||||
"remaining-square check did not use geometric size monotonicity");
|
||||
|
||||
// The fragmented profile has 21 empty cells, more than the area of the
|
||||
// remaining 2x2 square, but no two adjacent columns have two free rows.
|
||||
std::uint64_t filled_area = 0;
|
||||
for (auto const height: fragmented) {
|
||||
filled_area += height;
|
||||
}
|
||||
failures += expect(
|
||||
filled_area <= 36 - 4,
|
||||
"fragmented test state does not have sufficient total empty area");
|
||||
|
||||
for (auto const order: std::array<std::uint64_t, 5>{1, 2, 3, 4, 5}) {
|
||||
SearchCounters pruned_counters;
|
||||
SearchCounters baseline_counters;
|
||||
auto const pruned = search_solution_instrumented(
|
||||
order, pruned_counters, SearchPolicy::ascending,
|
||||
SymmetryBreaking::disabled, Pruning::all);
|
||||
auto const baseline = search_solution_instrumented(
|
||||
order, baseline_counters, SearchPolicy::ascending,
|
||||
SymmetryBreaking::disabled, Pruning::valley_capacity);
|
||||
failures += expect(
|
||||
pruned.squares().empty() == baseline.squares().empty(),
|
||||
"large-square pruning changed exhaustive order-" +
|
||||
std::to_string(order) + " feasibility");
|
||||
failures += expect(
|
||||
pruned_counters.search_nodes <= baseline_counters.search_nodes,
|
||||
"large-square pruning enlarged the exhaustive order-" +
|
||||
std::to_string(order) + " search");
|
||||
}
|
||||
|
||||
SearchCounters counters;
|
||||
static_cast<void>(search_solution_instrumented(
|
||||
5, counters, SearchPolicy::ascending, SymmetryBreaking::disabled,
|
||||
Pruning::large_square));
|
||||
failures += expect(
|
||||
counters.large_square_checks > 0 &&
|
||||
counters.large_square_prunes > 0 &&
|
||||
counters.prune_checks == counters.large_square_checks &&
|
||||
counters.prune_hits == counters.large_square_prunes,
|
||||
"large-square instrumentation did not count checks and prunes");
|
||||
return failures;
|
||||
}
|
||||
|
||||
auto test_solver_completion() -> int {
|
||||
int failures = 0;
|
||||
for (auto const order: std::array<std::uint64_t, 2>{1, 8}) {
|
||||
@@ -633,6 +700,9 @@ int main(int argc, char **argv) {
|
||||
if (test == "valley-capacity") {
|
||||
return test_valley_capacity_pruning();
|
||||
}
|
||||
if (test == "large-square") {
|
||||
return test_large_square_pruning();
|
||||
}
|
||||
std::cerr << "unknown test: " << test << '\n';
|
||||
return 2;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user