mamut_routing_tools.generation.binpacking¶
The minimum number of vehicles a load needs, computed rather than estimated.
CVRPLIB names an instance X-n101-k25, and that k is the exact optimum of
a bin-packing problem over the demands -- not a heuristic's fleet size and not
the continuous bound. It is published as a lower bound: the fleet is not fixed
and a solution may use more routes than the name says.
The continuous bound ceil(sum(q) / Q) is what this repository published
before, and it is genuinely weaker. On the v2 collection it understated the
truth on several bases: mamut-metz-n135-poi shipped num_vehicles_lb = 33
where the Martello-Toth bound alone already proves 37. A lower bound that is not
tight is not wrong, but it is worth less to anyone using it.
Exactness here is cheap because these demand distributions are kind: capacity is
ceil(r * mean demand) for a route-size target r of at least three, so
bins hold several items and first-fit-decreasing usually lands on the bound. On
the v2 collection L2 == FFD closed 104 of 110 bases with no search at all.
The six that did not are handled by a bounded search, and if even that does not
close, the bound is published as a bound and said to be one.
BinCount
dataclass
¶
What is known about the minimum bin count, and how firmly.
continuous_bound
¶
ceil(sum / capacity) -- the bound every load satisfies for free.
Source code in MAMUT-routing-tools/src/mamut_routing_tools/generation/binpacking.py
martello_toth_l2
¶
The L2 bound: count the items too big to share, then bound the rest.
For a threshold a, items larger than capacity - a need a bin each and
can take no item of size a or more alongside. Items above half capacity
cannot pair with each other, so each holds its own bin too, leaving a known
amount of room. Everything from a up to half capacity must fit in that
room or open new bins. Maximising over a gives the bound.
Source code in MAMUT-routing-tools/src/mamut_routing_tools/generation/binpacking.py
cardinality_bound
¶
How few bins can hold this many items, ignoring the sizes' arithmetic.
L2 reasons about weight and misses counting. Four thousand demands of 3 in
bins of 10 weigh 12 000 against 12 000 of capacity, so every weight-based
bound says 1200 -- but only three of them fit in a bin, so the answer is
1334. Small-demand instances are exactly where this bites, and
demand_type 2 and 3 produce them by design.
An item larger than capacity / (t + 1) cannot appear more than t
times in one bin, so counting such items and dividing by t bounds the
fleet. Maximising over t costs one pass per value and needs no search.
Source code in MAMUT-routing-tools/src/mamut_routing_tools/generation/binpacking.py
first_fit_decreasing
¶
Bins used by FFD -- a constructive upper bound.
Source code in MAMUT-routing-tools/src/mamut_routing_tools/generation/binpacking.py
best_fit_decreasing
¶
Bins used by BFD. Beats FFD often enough to be worth the second pass.
Source code in MAMUT-routing-tools/src/mamut_routing_tools/generation/binpacking.py
minimum_bins
¶
minimum_bins(demands: Sequence[int], capacity: int, *, node_budget: int = DEFAULT_NODE_BUDGET) -> BinCount
Fewest bins of capacity that hold demands.
Returns what was proven, not a guess: when the bounds meet, lower is the
optimum; when the search runs out of budget, lower is still a valid lower
bound and proven says so.
Source code in MAMUT-routing-tools/src/mamut_routing_tools/generation/binpacking.py
minimum_vehicles
¶
The published k: the best proven lower bound on the fleet.
Valid as a lower bound in every case, exact in all but the rare instance
whose search does not close. Callers that need to know which they got should
use :func:minimum_bins.