rectangleguillotine: fix column_generation_strips distance-1-cuts handling and search gaps#426
Merged
Merged
Conversation
…dling and search gaps column_generation_strips previously ignored maximum_distance_1_cuts and minimum_distance_1_cuts entirely when pricing candidate first-stage (1-cut) strips, and had several related bugs uncovered while building out coverage for every (number_of_stages, cut_type, orientation) combination: - maximum_distance_1_cuts: cap the per-column search width (first_stage_available_width), so no generated strip can exceed it. - minimum_distance_1_cuts: pad a strip's boundary up to the minimum with waste (mirroring how tree_search pushes 1-cut positions outward instead of rejecting narrower placements), and stop searching narrower widths once they would only be padded back up anyway. The exact-width generators (generate_1e_patterns and the eager generate_all_columns_1r_patterns/ 2h_patterns) have no depth-3 narrowing cut, so a segment narrower than the minimum can't be padded without a dimension mismatch: skip those instead. - generate_all_columns_1r_patterns/2h_patterns were also missing a bound check on the (possibly rotated) item width against the bin width, crashing SolutionBuilder::add_node whenever a rotated item was wider than the bin. - generate_lower_stage_patterns charged the master LP the strip's unpadded width while building its reconstructed geometry at the padded width, letting the LP pack more strips than physically fit; both now agree. - generate_lower_stage_patterns's sub-instance previously inherited minimum_distance_1_cuts/maximum_distance_1_cuts/minimum_distance_2_cuts and maximum_number_2_cuts unchanged from the outer instance. Since the sub-instance's own 1-cuts are the outer's 2-cuts (and its 2-cuts are the outer's 3-cuts, which have no matching outer constraint), it now inherits minimum_distance_1_cuts from the outer's minimum_distance_2_cuts and resets the rest, fixing several cases where the recursion under- or over-constrained the sub-problem. - Every generated strip is now also considered directly as a candidate whole-bin solution (the rest of the bin auto-fills as waste), so the algorithm can find the optimum when it consists of a single strip even if the master's combinatorial search over multiple strips never isolates it. - The pricing solver now runs one extra pass per branch with all duals forced to zero (real profit instead of reduced profit), forcing the lower-stage recursion to run regardless of the "a good column was already found" shortcut. This guarantees the real-profit-optimal column for the remaining capacity is always generated, regardless of where the master's dual values happen to be at that point. tree_search's minimum_distance_1_cuts handling had a matching bug: for 2-staged instances, insertion.x1 (checked against minimum_distance_1_cuts) is not a real 1-cut once the possibly-flipped first-stage decision is made, while the true width axis (insertion.y2) was only checked against minimum_distance_2_cuts, which defaults to unconstrained. minimum_distance_1_ cuts_/minimum_distance_2_cuts_ are now precomputed once per BranchingScheme (0/minimum_distance_1_cuts for 2-staged instances, unchanged otherwise) instead of re-derived on every node. Added 32 knapsack_[23][ehnr]v[or][_min1cut] test instances, wired into both RectangleGuillotineTreeSearchTest and RectangleGuillotineColumnGenerationStrips Test, each verified to genuinely require its stated stage count, cut type, and (for rotatable variants) actual rotation, with the min1cut variant always yielding a different optimum from its base. Also added knapsack_2nho_2, a captured reduced-cost sub-problem that pinned a since-fixed automatic_stop early-termination gap in the limited discrepancy search, and switched RectangleGuillotineColumnGenerationStripsTest to NotAnytimeSequential so it exercises that code path. Updated the knapsack_3rvo_min1cut/knapsack_3rvr_ min1cut reference certificates to the better solutions column_generation_ strips now finds, and dropped those two from RectangleGuillotineTreeSearchTest since tree_search's search cannot reach them (traced to overly permissive dominance-based pruning in BranchingScheme::dominates, not yet fixed).
eda5f9e to
25337cd
Compare
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.
column_generation_strips previously ignored maximum_distance_1_cuts and minimum_distance_1_cuts entirely when pricing candidate first-stage (1-cut) strips, and had several related bugs uncovered while building out coverage for every (number_of_stages, cut_type, orientation) combination:
tree_search's minimum_distance_1_cuts handling had a matching bug: for 2-staged instances, insertion.x1 (checked against minimum_distance_1_cuts) is not a real 1-cut once the possibly-flipped first-stage decision is made, while the true width axis (insertion.y2) was only checked against minimum_distance_2_cuts, which defaults to unconstrained. minimum_distance_1_ cuts_/minimum_distance_2_cuts_ are now precomputed once per BranchingScheme (0/minimum_distance_1_cuts for 2-staged instances, unchanged otherwise) instead of re-derived on every node.
Added 32 knapsack_[23][ehnr]v[or][min1cut] test instances, wired into both RectangleGuillotineTreeSearchTest and RectangleGuillotineColumnGenerationStrips Test, each verified to genuinely require its stated stage count, cut type, and (for rotatable variants) actual rotation, with the min1cut variant always yielding a different optimum from its base. Also added knapsack_2nho_2, a captured reduced-cost sub-problem that pinned a since-fixed automatic_stop early-termination gap in the limited discrepancy search, and switched RectangleGuillotineColumnGenerationStripsTest to NotAnytimeSequential so it exercises that code path. Updated the knapsack_3rvo_min1cut/knapsack_3rvr min1cut reference certificates to the better solutions column_generation_ strips now finds, and dropped those two from RectangleGuillotineTreeSearchTest since tree_search's search cannot reach them (traced to overly permissive dominance-based pruning in BranchingScheme::dominates, not yet fixed).