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:
+52
-2
@@ -21,8 +21,9 @@ and `--candidate-order`. The choices are `ascending`, `descending`, and
|
||||
The production default also removes equivalent D4 board orientations by
|
||||
constraining the unique unit square. Pass `--no-symmetry` to obtain an
|
||||
otherwise identical unconstrained baseline.
|
||||
The production default also applies the valley-capacity rule described below.
|
||||
Pass `--no-pruning` to obtain an otherwise identical unpruned search.
|
||||
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.
|
||||
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:
|
||||
|
||||
@@ -107,6 +108,55 @@ The rule reduced nodes by 7.8% for order 7 and 7.1% for order 8. Its low
|
||||
per-node cost also reduced median counted solve time by 10.3% and 9.9%,
|
||||
respectively, so it remains enabled by default.
|
||||
|
||||
## Remaining-large-square pruning
|
||||
|
||||
For a remaining side `k`, a skyline can contain an empty `k`-by-`k` box only
|
||||
if it has `k` consecutive columns whose filled heights are no greater than the
|
||||
board height minus `k`. A single linear scan tracks qualifying consecutive
|
||||
columns. If no such run exists, the square cannot be placed and the state is
|
||||
infeasible even when its total empty area is sufficient.
|
||||
|
||||
Empty-box feasibility is monotonic in the side length: a box which fits the
|
||||
largest remaining square also fits every smaller remaining size. The solver
|
||||
therefore invokes only one `O(board width)` scan per surviving node, after the
|
||||
cheaper valley-capacity check. It does not attempt an unproven multiplicity
|
||||
bound. `--pruning large-square` measures the rule independently;
|
||||
`--pruning valley-capacity` provides the production baseline without it.
|
||||
Instrumented output records `large_square_checks` and
|
||||
`large_square_prunes`.
|
||||
|
||||
Measurements used the issue #15 working tree based on commit `e27427d`, Apple
|
||||
Clang 21.0.0, `-O3 -DNDEBUG`, macOS arm64, one worker, one warm-up, and seven
|
||||
measured repetitions. Counter-free and counted runs were interleaved; the
|
||||
table reports the counter-free production instantiation. The baseline keeps
|
||||
valley-capacity pruning enabled, so it isolates the new rule. All results
|
||||
passed independent validation and counters were stable:
|
||||
|
||||
| Search | Large-square check | Median solve | Nodes | Checks | Prunes |
|
||||
| --- | --- | ---: | ---: | ---: | ---: |
|
||||
| Order 7 exhaustive | enabled | 0.946 s | 13,221,239 | 6,163,390 | 227,321 |
|
||||
| Order 7 exhaustive | disabled | 0.947 s | 13,833,048 | 0 | 0 |
|
||||
| Order 8 first solution | enabled | 0.195 s | 2,597,678 | 1,063,472 | 38,177 |
|
||||
| Order 8 first solution | disabled | 0.197 s | 2,724,096 | 0 | 0 |
|
||||
| Order 9 direct | enabled | 1.225 s | 14,840,146 | 5,592,843 | 54,900 |
|
||||
| Order 9 direct | disabled | 1.208 s | 14,995,127 | 0 | 0 |
|
||||
|
||||
The rule reduced nodes by 4.4% for order 7, 4.6% for order 8, and 1.0% for
|
||||
direct order 9. Counter-free median time improved by 0.12% and 0.89% on the
|
||||
two distinct searches in the public benchmark set. Public order 9 constructs
|
||||
from the improving order-8 search. Direct order 9, which bypasses that public
|
||||
route, regressed by 1.39%; it remains documented as a caution for future
|
||||
policy tuning.
|
||||
|
||||
Checking only every fourth placement depth was also measured. It retained
|
||||
fewer prunes and was slower than checking every surviving node by 2.2% for
|
||||
order 7, 2.3% for order 8, and 0.6% for direct order 9, so the periodic
|
||||
schedule was rejected. Incremental maintenance would need additional
|
||||
per-size window state and undo logic for an `O(W)` scan whose public net cost
|
||||
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.
|
||||
|
||||
## D4 board symmetry
|
||||
|
||||
Every solution contains exactly one 1-by-1 square. Rotations and reflections
|
||||
|
||||
@@ -27,6 +27,7 @@ if(BUILD_TESTING)
|
||||
add_test(NAME skyline-search COMMAND partridge_tests skyline-search)
|
||||
add_test(NAME d4-symmetry COMMAND partridge_tests d4-symmetry)
|
||||
add_test(NAME valley-capacity COMMAND partridge_tests valley-capacity)
|
||||
add_test(NAME large-square COMMAND partridge_tests large-square)
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
if(Python3_Interpreter_FOUND)
|
||||
add_test(NAME benchmark-format
|
||||
|
||||
+27
-6
@@ -92,13 +92,24 @@ namespace {
|
||||
}
|
||||
|
||||
auto boolean(bool value) -> char const * { return value ? "true" : "false"; }
|
||||
|
||||
auto pruning_name(Pruning const pruning) -> char const * {
|
||||
switch (pruning) {
|
||||
case Pruning::all: return "all";
|
||||
case Pruning::valley_capacity: return "valley-capacity";
|
||||
case Pruning::large_square: return "large-square";
|
||||
case Pruning::disabled: return "none";
|
||||
}
|
||||
assert(false);
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3 || argc > 7) {
|
||||
std::cerr << "usage: partridge_benchmark ORDER counters|plain "
|
||||
"[ascending|descending|best-fit] [public|direct] "
|
||||
"[d4|none] [valley-capacity|none]\n";
|
||||
"[d4|none] [all|valley-capacity|large-square|none]\n";
|
||||
return 2;
|
||||
}
|
||||
auto const order = static_cast<std::uint64_t>(std::strtoull(argv[1], nullptr, 10));
|
||||
@@ -136,12 +147,19 @@ int main(int argc, char **argv) {
|
||||
return 2;
|
||||
}
|
||||
auto const pruning =
|
||||
argc < 7 || std::string_view(argv[6]) == "valley-capacity"
|
||||
argc < 7 || std::string_view(argv[6]) == "all"
|
||||
? Pruning::all
|
||||
: std::string_view(argv[6]) == "valley-capacity"
|
||||
? Pruning::valley_capacity
|
||||
: std::string_view(argv[6]) == "large-square"
|
||||
? Pruning::large_square
|
||||
: Pruning::disabled;
|
||||
if (argc == 7 && std::string_view(argv[6]) != "valley-capacity" &&
|
||||
if (argc == 7 && std::string_view(argv[6]) != "all" &&
|
||||
std::string_view(argv[6]) != "valley-capacity" &&
|
||||
std::string_view(argv[6]) != "large-square" &&
|
||||
std::string_view(argv[6]) != "none") {
|
||||
std::cerr << "pruning mode must be valley-capacity or none\n";
|
||||
std::cerr << "pruning mode must be all, valley-capacity, "
|
||||
"large-square, or none\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -177,8 +195,7 @@ int main(int argc, char **argv) {
|
||||
<< (symmetry == SymmetryBreaking::d4_unit_square ? "d4" : "none")
|
||||
<< "\""
|
||||
<< ",\"pruning\":\""
|
||||
<< (pruning == Pruning::valley_capacity ? "valley-capacity" : "none")
|
||||
<< "\""
|
||||
<< pruning_name(pruning) << "\""
|
||||
<< ",\"solved\":" << boolean(!timed.result.squares().empty())
|
||||
<< ",\"valid\":" << boolean(validation_ok)
|
||||
<< ",\"timing_seconds\":{\"solve\":" << timed.search_seconds
|
||||
@@ -195,6 +212,10 @@ int main(int argc, char **argv) {
|
||||
<< counters.valley_capacity_checks
|
||||
<< ",\"valley_capacity_prunes\":"
|
||||
<< counters.valley_capacity_prunes
|
||||
<< ",\"large_square_checks\":"
|
||||
<< counters.large_square_checks
|
||||
<< ",\"large_square_prunes\":"
|
||||
<< counters.large_square_prunes
|
||||
<< ",\"generated_tasks\":" << counters.generated_tasks
|
||||
<< ",\"completed_tasks\":" << counters.completed_tasks << "}}\n";
|
||||
return validation_ok ? 0 : 1;
|
||||
|
||||
+10
-4
@@ -238,10 +238,16 @@ def main():
|
||||
action="store_true",
|
||||
help="disable unique-unit-square D4 symmetry breaking",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pruning",
|
||||
choices=("all", "valley-capacity", "large-square", "none"),
|
||||
default="all",
|
||||
help="select independently measurable pruning rules",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-pruning",
|
||||
action="store_true",
|
||||
help="disable valley-capacity pruning",
|
||||
help="disable all pruning (compatibility alias for --pruning none)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--measure-overhead",
|
||||
@@ -275,7 +281,7 @@ def main():
|
||||
args.search_policy,
|
||||
"direct" if args.direct_search else "public",
|
||||
"none" if args.no_symmetry else "d4",
|
||||
"none" if args.no_pruning else "valley-capacity",
|
||||
"none" if args.no_pruning else args.pruning,
|
||||
args.timeout,
|
||||
)
|
||||
for trial in range(args.repetitions):
|
||||
@@ -288,7 +294,7 @@ def main():
|
||||
args.search_policy,
|
||||
"direct" if args.direct_search else "public",
|
||||
"none" if args.no_symmetry else "d4",
|
||||
"none" if args.no_pruning else "valley-capacity",
|
||||
"none" if args.no_pruning else args.pruning,
|
||||
args.timeout,
|
||||
)
|
||||
)
|
||||
@@ -318,7 +324,7 @@ def main():
|
||||
"candidate_order": args.search_policy,
|
||||
"search_route": "direct" if args.direct_search else "public",
|
||||
"symmetry_breaking": "none" if args.no_symmetry else "d4",
|
||||
"pruning": "none" if args.no_pruning else "valley-capacity",
|
||||
"pruning": "none" if args.no_pruning else args.pruning,
|
||||
"stdout": "captured; rendered grid suppressed by probe",
|
||||
},
|
||||
"cases": cases,
|
||||
|
||||
@@ -152,6 +152,8 @@ namespace {
|
||||
size_t prune_hits = 0;
|
||||
size_t valley_capacity_checks = 0;
|
||||
size_t valley_capacity_prunes = 0;
|
||||
size_t large_square_checks = 0;
|
||||
size_t large_square_prunes = 0;
|
||||
size_t generated_tasks = 0;
|
||||
size_t completed_tasks = 0;
|
||||
};
|
||||
@@ -172,8 +174,10 @@ namespace {
|
||||
|
||||
/** Cheap necessary conditions applied before branching at a search node. */
|
||||
enum class Pruning {
|
||||
disabled,
|
||||
valley_capacity,
|
||||
disabled = 0,
|
||||
valley_capacity = 1,
|
||||
large_square = 2,
|
||||
all = 3,
|
||||
};
|
||||
|
||||
/** Return whether a cell is the canonical representative of its D4 orbit.
|
||||
@@ -259,7 +263,46 @@ namespace {
|
||||
return available_area >= required_area;
|
||||
}
|
||||
|
||||
template<bool Instrument, bool BreakD4Symmetry, bool PruneValleyCapacity>
|
||||
/** Return whether the skyline contains an empty box for a square.
|
||||
*
|
||||
* A side-k square fits exactly when k consecutive columns have heights no
|
||||
* greater than board height minus k. Tracking the current qualifying run
|
||||
* checks this in one pass without allocations.
|
||||
*/
|
||||
[[nodiscard]] auto skyline_has_empty_square(
|
||||
std::vector<size_t> const &skyline, size_t const side) noexcept -> bool {
|
||||
assert(side > 0);
|
||||
assert(side <= skyline.size());
|
||||
auto const maximum_height = skyline.size() - side;
|
||||
size_t run = 0;
|
||||
for (auto const height: skyline) {
|
||||
run = height <= maximum_height ? run + 1 : 0;
|
||||
if (run == side) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Return whether the largest remaining square has any feasible position.
|
||||
*
|
||||
* Feasible empty boxes are monotonic in the side length: a box which fits
|
||||
* the largest remaining square also fits every smaller one. It is therefore
|
||||
* sufficient to test only the largest size with non-zero multiplicity.
|
||||
*/
|
||||
[[nodiscard]] auto remaining_large_square_fits(
|
||||
std::vector<size_t> const &skyline, Avail const &available) noexcept
|
||||
-> bool {
|
||||
for (auto side = available.size() - 1; side != 0; --side) {
|
||||
if (available[side] != 0) {
|
||||
return skyline_has_empty_square(skyline, side);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool Instrument, bool BreakD4Symmetry, bool PruneValleyCapacity,
|
||||
bool PruneLargeSquare>
|
||||
auto search_skyline(size_t const n, size_t const length,
|
||||
SearchPolicy const policy,
|
||||
std::vector<size_t> &skyline, Avail &available,
|
||||
@@ -288,6 +331,19 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if constexpr (PruneLargeSquare) {
|
||||
if constexpr (Instrument) {
|
||||
++counters->prune_checks;
|
||||
++counters->large_square_checks;
|
||||
}
|
||||
if (!remaining_large_square_fits(skyline, available)) {
|
||||
if constexpr (Instrument) {
|
||||
++counters->prune_hits;
|
||||
++counters->large_square_prunes;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto const largest =
|
||||
std::min({n, valley.width, length - valley.height});
|
||||
auto try_side = [&](size_t const side) {
|
||||
@@ -320,7 +376,8 @@ namespace {
|
||||
side, valley.height + side);
|
||||
squares.emplace_back(valley.x + valley.height * length, side);
|
||||
|
||||
if (search_skyline<Instrument, BreakD4Symmetry, PruneValleyCapacity>(
|
||||
if (search_skyline<Instrument, BreakD4Symmetry, PruneValleyCapacity,
|
||||
PruneLargeSquare>(
|
||||
n, length, policy, skyline, available, squares, counters)) {
|
||||
return true;
|
||||
}
|
||||
@@ -367,7 +424,8 @@ namespace {
|
||||
* candidates and updating up to n columns for each costs O(n^2), for
|
||||
* O(board width + n^2) local work per node and the same total state.
|
||||
*/
|
||||
template<bool Instrument, bool BreakD4Symmetry, bool PruneValleyCapacity>
|
||||
template<bool Instrument, bool BreakD4Symmetry, bool PruneValleyCapacity,
|
||||
bool PruneLargeSquare>
|
||||
auto search_solution_impl(size_t const n, SearchPolicy const policy,
|
||||
SearchCounters *const counters) noexcept
|
||||
-> Results {
|
||||
@@ -380,29 +438,50 @@ namespace {
|
||||
std::vector<Square> squares;
|
||||
squares.reserve(length);
|
||||
static_cast<void>(
|
||||
search_skyline<Instrument, BreakD4Symmetry, PruneValleyCapacity>(
|
||||
search_skyline<Instrument, BreakD4Symmetry, PruneValleyCapacity,
|
||||
PruneLargeSquare>(
|
||||
n, length, policy, skyline, available, squares, counters));
|
||||
|
||||
return {length, std::move(squares)};
|
||||
}
|
||||
|
||||
template<bool Instrument, bool BreakD4Symmetry>
|
||||
auto search_solution_dispatch(size_t const n, SearchPolicy const policy,
|
||||
Pruning const pruning,
|
||||
SearchCounters *const counters) noexcept
|
||||
-> Results {
|
||||
switch (pruning) {
|
||||
case Pruning::all:
|
||||
return search_solution_impl<Instrument, BreakD4Symmetry, true, true>(
|
||||
n, policy, counters);
|
||||
case Pruning::valley_capacity:
|
||||
return search_solution_impl<Instrument, BreakD4Symmetry, true, false>(
|
||||
n, policy, counters);
|
||||
case Pruning::large_square:
|
||||
return search_solution_impl<Instrument, BreakD4Symmetry, false, true>(
|
||||
n, policy, counters);
|
||||
case Pruning::disabled:
|
||||
return search_solution_impl<Instrument, BreakD4Symmetry, false, false>(
|
||||
n, policy, counters);
|
||||
}
|
||||
assert(false);
|
||||
return search_solution_impl<Instrument, BreakD4Symmetry, false, false>(
|
||||
n, policy, counters);
|
||||
}
|
||||
|
||||
auto search_solution(
|
||||
size_t const n,
|
||||
SearchPolicy const policy = SearchPolicy::ascending,
|
||||
SymmetryBreaking const symmetry =
|
||||
SymmetryBreaking::d4_unit_square,
|
||||
Pruning const pruning = Pruning::valley_capacity) noexcept
|
||||
Pruning const pruning = Pruning::all) noexcept
|
||||
-> Results {
|
||||
if (symmetry == SymmetryBreaking::d4_unit_square) {
|
||||
if (pruning == Pruning::valley_capacity) {
|
||||
return search_solution_impl<false, true, true>(n, policy, nullptr);
|
||||
return search_solution_dispatch<false, true>(
|
||||
n, policy, pruning, nullptr);
|
||||
}
|
||||
return search_solution_impl<false, true, false>(n, policy, nullptr);
|
||||
}
|
||||
if (pruning == Pruning::valley_capacity) {
|
||||
return search_solution_impl<false, false, true>(n, policy, nullptr);
|
||||
}
|
||||
return search_solution_impl<false, false, false>(n, policy, nullptr);
|
||||
return search_solution_dispatch<false, false>(
|
||||
n, policy, pruning, nullptr);
|
||||
}
|
||||
|
||||
auto search_solution_instrumented(size_t const n,
|
||||
@@ -412,21 +491,15 @@ namespace {
|
||||
SymmetryBreaking const symmetry =
|
||||
SymmetryBreaking::d4_unit_square,
|
||||
Pruning const pruning =
|
||||
Pruning::valley_capacity) noexcept
|
||||
Pruning::all) noexcept
|
||||
-> Results {
|
||||
counters = {};
|
||||
if (symmetry == SymmetryBreaking::d4_unit_square) {
|
||||
if (pruning == Pruning::valley_capacity) {
|
||||
return search_solution_impl<true, true, true>(
|
||||
n, policy, &counters);
|
||||
return search_solution_dispatch<true, true>(
|
||||
n, policy, pruning, &counters);
|
||||
}
|
||||
return search_solution_impl<true, true, false>(n, policy, &counters);
|
||||
}
|
||||
if (pruning == Pruning::valley_capacity) {
|
||||
return search_solution_impl<true, false, true>(
|
||||
n, policy, &counters);
|
||||
}
|
||||
return search_solution_impl<true, false, false>(n, policy, &counters);
|
||||
return search_solution_dispatch<true, false>(
|
||||
n, policy, pruning, &counters);
|
||||
}
|
||||
|
||||
/** Construct an odd-order solution from its even-order predecessor. */
|
||||
@@ -464,7 +537,7 @@ namespace {
|
||||
SearchPolicy const policy = SearchPolicy::ascending,
|
||||
SymmetryBreaking const symmetry =
|
||||
SymmetryBreaking::d4_unit_square,
|
||||
Pruning const pruning = Pruning::valley_capacity) noexcept -> Results {
|
||||
Pruning const pruning = Pruning::all) noexcept -> Results {
|
||||
if (uses_odd_construction(n)) {
|
||||
return construct_odd_solution(
|
||||
n, search_solution(n - 1, policy, symmetry, pruning));
|
||||
@@ -479,7 +552,7 @@ namespace {
|
||||
SymmetryBreaking const symmetry =
|
||||
SymmetryBreaking::d4_unit_square,
|
||||
Pruning const pruning =
|
||||
Pruning::valley_capacity) noexcept
|
||||
Pruning::all) noexcept
|
||||
-> Results {
|
||||
if (uses_odd_construction(n)) {
|
||||
return construct_odd_solution(
|
||||
|
||||
@@ -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