FA-94996 / Warehouse slotting and bin packing / Open access
Pallet build: tare ignored in weight cap · case 01
Weight-limited pallets exceed the gross limit by the pallet tare.
ROOT CAUSE
The weight bound divides the gross limit by case weight without subtracting pallet tare.
VERIFIED REPAIR
Subtract the 25 kg tare before computing how many cases the weight limit allows.
Unsuccessful approach: Subtracting one extra case as a safety margin under-fills pallets.
Case contract
case [l, w, h, kg], pallet [L, W]; cases stay upright but may rotate 90 degrees; all layers share one orientation. Pallet base adds 15 height and 25 kg tare. Cases per pallet are bounded by whole layers within max_h and by max_kg (a partial top layer is allowed). Return [per_layer, layers used, cases, height, weight].
Why this case matters
Pallet build limits govern reserve storage heights and floor loading.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(case, pallet, max_h, max_kg):
l, w, h, kg = case
L, W = pallet
per_layer = max((L // l) * (W // w), (L // w) * (W // l))
if per_layer == 0:
return [0, 0, 0, 15, 25]
layers = (max_h - 15) // h
by_weight = max_kg // kg
cases = min(per_layer * layers, by_weight)
if cases <= 0:
return [per_layer, 0, 0, 15, 25]
full = -(-cases // per_layer)
return [per_layer, full, cases, 15 + full * h, 25 + cases * kg]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['generated mix 1', [[50, 15, 10, 12], [120, 100], 190, 1025], [16, 6, 83, 75, 1021]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['generated mix 15', [[50, 20, 30, 8], [120, 80], 115, 800], [8, 3, 24, 105, 217]]], [['generated mix 24', [[25, 30, 10, 12], [120, 100], 115, 1025], [16, 6, 83, 75, 1021]], ['generated mix 20', [[20, 20, 25, 20], [120, 100], 115, 1200], [30, 2, 58, 65, 1185]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 9', [[50, 20, 25, 12], [120, 100], 115, 1200], [12, 4, 48, 115, 601]], ['generated mix 17', [[30, 30, 30, 8], [120, 80], 165, 500], [8, 5, 40, 165, 345]]], [['generated mix 35', [[25, 20, 10, 12], [120, 80], 140, 800], [18, 4, 64, 55, 793]], ['generated mix 29', [[50, 30, 30, 20], [120, 100], 190, 300], [8, 2, 13, 75, 285]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 20', [[20, 20, 25, 20], [120, 100], 115, 1200], [30, 2, 58, 65, 1185]], ['generated mix 28', [[20, 30, 20, 12], [120, 80], 140, 800], [16, 4, 64, 95, 793]]], [['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 36', [[25, 30, 20, 12], [120, 80], 115, 500], [12, 4, 39, 95, 493]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 31', [[40, 15, 25, 5], [120, 100], 190, 1025], [18, 7, 126, 190, 655]], ['generated mix 40', [[20, 15, 30, 20], [120, 80], 190, 500], [32, 1, 23, 45, 485]]], [['generated mix 58', [[50, 25, 10, 12], [120, 100], 140, 500], [8, 5, 39, 65, 493]], ['generated mix 42', [[40, 30, 20, 5], [120, 80], 165, 300], [8, 7, 55, 155, 300]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 51', [[25, 15, 10, 8], [120, 80], 165, 500], [24, 3, 59, 45, 497]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| weight bound partial layer | [9, 3, 21, 90, 445] | [9, 3, 20, 90, 425] | Failed |
| generated mix 1 | [16, 6, 85, 75, 1045] | [16, 6, 83, 75, 1021] | Failed |
| listed orientation wins | [20, 5, 100, 115, 525] | [20, 5, 100, 115, 525] | Passed |
| height exact | [9, 5, 45, 140, 115] | [9, 5, 45, 140, 115] | Passed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| rotation wins | [8, 5, 40, 115, 225] | [8, 5, 40, 115, 225] | Passed |
| generated mix 7 | [8, 6, 48, 135, 265] | [8, 6, 48, 135, 265] | Passed |
| generated mix 15 | [8, 3, 24, 105, 217] | [8, 3, 24, 105, 217] | Passed |
SHA-256 / bc9f62e5b3603c81eb67793ba533bb81659cb79dac4e6c6e2cff93a245124da4
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(case, pallet, max_h, max_kg):
l, w, h, kg = case
L, W = pallet
per_layer = max((L // l) * (W // w), (L // w) * (W // l))
if per_layer == 0:
return [0, 0, 0, 15, 25]
layers = (max_h - 15) // h
by_weight = (max_kg - 25) // kg - 1
cases = min(per_layer * layers, by_weight)
if cases <= 0:
return [per_layer, 0, 0, 15, 25]
full = -(-cases // per_layer)
return [per_layer, full, cases, 15 + full * h, 25 + cases * kg]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['generated mix 1', [[50, 15, 10, 12], [120, 100], 190, 1025], [16, 6, 83, 75, 1021]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['generated mix 15', [[50, 20, 30, 8], [120, 80], 115, 800], [8, 3, 24, 105, 217]]], [['generated mix 24', [[25, 30, 10, 12], [120, 100], 115, 1025], [16, 6, 83, 75, 1021]], ['generated mix 20', [[20, 20, 25, 20], [120, 100], 115, 1200], [30, 2, 58, 65, 1185]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 9', [[50, 20, 25, 12], [120, 100], 115, 1200], [12, 4, 48, 115, 601]], ['generated mix 17', [[30, 30, 30, 8], [120, 80], 165, 500], [8, 5, 40, 165, 345]]], [['generated mix 35', [[25, 20, 10, 12], [120, 80], 140, 800], [18, 4, 64, 55, 793]], ['generated mix 29', [[50, 30, 30, 20], [120, 100], 190, 300], [8, 2, 13, 75, 285]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 20', [[20, 20, 25, 20], [120, 100], 115, 1200], [30, 2, 58, 65, 1185]], ['generated mix 28', [[20, 30, 20, 12], [120, 80], 140, 800], [16, 4, 64, 95, 793]]], [['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 36', [[25, 30, 20, 12], [120, 80], 115, 500], [12, 4, 39, 95, 493]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 31', [[40, 15, 25, 5], [120, 100], 190, 1025], [18, 7, 126, 190, 655]], ['generated mix 40', [[20, 15, 30, 20], [120, 80], 190, 500], [32, 1, 23, 45, 485]]], [['generated mix 58', [[50, 25, 10, 12], [120, 100], 140, 500], [8, 5, 39, 65, 493]], ['generated mix 42', [[40, 30, 20, 5], [120, 80], 165, 300], [8, 7, 55, 155, 300]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 51', [[25, 15, 10, 8], [120, 80], 165, 500], [24, 3, 59, 45, 497]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| weight bound partial layer | [9, 3, 19, 90, 405] | [9, 3, 20, 90, 425] | Failed |
| generated mix 1 | [16, 6, 82, 75, 1009] | [16, 6, 83, 75, 1021] | Failed |
| listed orientation wins | [20, 5, 100, 115, 525] | [20, 5, 100, 115, 525] | Passed |
| height exact | [9, 5, 45, 140, 115] | [9, 5, 45, 140, 115] | Passed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| rotation wins | [8, 5, 40, 115, 225] | [8, 5, 40, 115, 225] | Passed |
| generated mix 7 | [8, 6, 48, 135, 265] | [8, 6, 48, 135, 265] | Passed |
| generated mix 15 | [8, 3, 24, 105, 217] | [8, 3, 24, 105, 217] | Passed |
SHA-256 / dccc728a068662e864e1dfc0d717614e987349c365cd54621871e6508cafc728
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(case, pallet, max_h, max_kg):
l, w, h, kg = case
L, W = pallet
per_layer = max((L // l) * (W // w), (L // w) * (W // l))
if per_layer == 0:
return [0, 0, 0, 15, 25]
layers = (max_h - 15) // h
by_weight = (max_kg - 25) // kg
cases = min(per_layer * layers, by_weight)
if cases <= 0:
return [per_layer, 0, 0, 15, 25]
full = -(-cases // per_layer)
return [per_layer, full, cases, 15 + full * h, 25 + cases * kg]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['generated mix 1', [[50, 15, 10, 12], [120, 100], 190, 1025], [16, 6, 83, 75, 1021]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['generated mix 15', [[50, 20, 30, 8], [120, 80], 115, 800], [8, 3, 24, 105, 217]]], [['generated mix 24', [[25, 30, 10, 12], [120, 100], 115, 1025], [16, 6, 83, 75, 1021]], ['generated mix 20', [[20, 20, 25, 20], [120, 100], 115, 1200], [30, 2, 58, 65, 1185]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 9', [[50, 20, 25, 12], [120, 100], 115, 1200], [12, 4, 48, 115, 601]], ['generated mix 17', [[30, 30, 30, 8], [120, 80], 165, 500], [8, 5, 40, 165, 345]]], [['generated mix 35', [[25, 20, 10, 12], [120, 80], 140, 800], [18, 4, 64, 55, 793]], ['generated mix 29', [[50, 30, 30, 20], [120, 100], 190, 300], [8, 2, 13, 75, 285]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 20', [[20, 20, 25, 20], [120, 100], 115, 1200], [30, 2, 58, 65, 1185]], ['generated mix 28', [[20, 30, 20, 12], [120, 80], 140, 800], [16, 4, 64, 95, 793]]], [['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 36', [[25, 30, 20, 12], [120, 80], 115, 500], [12, 4, 39, 95, 493]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 31', [[40, 15, 25, 5], [120, 100], 190, 1025], [18, 7, 126, 190, 655]], ['generated mix 40', [[20, 15, 30, 20], [120, 80], 190, 500], [32, 1, 23, 45, 485]]], [['generated mix 58', [[50, 25, 10, 12], [120, 100], 140, 500], [8, 5, 39, 65, 493]], ['generated mix 42', [[40, 30, 20, 5], [120, 80], 165, 300], [8, 7, 55, 155, 300]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 51', [[25, 15, 10, 8], [120, 80], 165, 500], [24, 3, 59, 45, 497]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| weight bound partial layer | [9, 3, 20, 90, 425] | [9, 3, 20, 90, 425] | Passed |
| generated mix 1 | [16, 6, 83, 75, 1021] | [16, 6, 83, 75, 1021] | Passed |
| listed orientation wins | [20, 5, 100, 115, 525] | [20, 5, 100, 115, 525] | Passed |
| height exact | [9, 5, 45, 140, 115] | [9, 5, 45, 140, 115] | Passed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| rotation wins | [8, 5, 40, 115, 225] | [8, 5, 40, 115, 225] | Passed |
| generated mix 7 | [8, 6, 48, 135, 265] | [8, 6, 48, 135, 265] | Passed |
| generated mix 15 | [8, 3, 24, 105, 217] | [8, 3, 24, 105, 217] | Passed |
SHA-256 / 3dd194a702769bbffb75ecb7ca0170245886aaf1764f8defe2785c5af11cca9b
Verification & scope
Uniform-case, single-orientation layer model; no interlocking patterns. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:52:09.640612+00:00.
Case digest / e5d07377146111e2a1c4dd7860b6ea0bbda8c6ce403571999f55d71e006cd051