Coverage for moptipyapps / binpacking2d / objectives / bin_count_and_empty.py: 67%
24 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 04:40 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 04:40 +0000
1"""
2An objective function indirectly minimizing the number of bins in packings.
4This objective function first computes the number of bins used. Let's call it
5`n_bins`. We know the total number of items,
6:attr:`~moptipyapps.binpacking2d.instance.Instance.n_items`, as
7well (because this is also the number of rows in the packing).
8Now we return `(n_items * (n_bins - 1)) + min_items`,
9where `min_items` is the number of items in the bin with the fewest items.
11This is similar to :mod:`~moptipyapps.binpacking2d.objectives.\
12bin_count_and_last_small`, but instead of focussing on the very last bin, it
13uses the minimum element count over all bins. It has the same lower- and upper
14bound, though.
15"""
16from typing import Final
18import numba # type: ignore
19import numpy as np
21from moptipyapps.binpacking2d.instance import Instance
22from moptipyapps.binpacking2d.objectives.bin_count_and_last_empty import (
23 BinCountAndLastEmpty,
24)
25from moptipyapps.binpacking2d.packing import IDX_BIN
28@numba.njit(cache=True, inline="always", fastmath=True, boundscheck=False)
29def bin_count_and_empty(y: np.ndarray, temp: np.ndarray) -> int:
30 """
31 Get the number of bins and number of elements in the emptiest one.
33 We compute the total number of bins minus 1 and multiply it with the
34 number of items. We then add the smallest number of elements in any
35 bin.
37 :param y: the packing
38 :param temp: the temporary array
39 :return: the objective value
41 >>> tempa = np.empty(10, int)
42 >>> bin_count_and_empty(np.array([[1, 1, 10, 10, 20, 20],
43 ... [1, 1, 30, 30, 40, 40],
44 ... [1, 1, 20, 20, 30, 30]], int), tempa)
45 3
46 >>> bin_count_and_empty(np.array([[1, 1, 10, 10, 20, 20],
47 ... [1, 2, 30, 30, 40, 40], # bin 2!
48 ... [1, 1, 20, 20, 30, 30]], int), tempa)
49 4
50 >>> bin_count_and_empty(np.array([[1, 2, 10, 10, 20, 20], # bin 2!
51 ... [1, 2, 30, 30, 40, 40], # bin 2!
52 ... [1, 1, 20, 20, 30, 30]], int), tempa)
53 4
54 >>> bin_count_and_empty(np.array([[1, 3, 10, 10, 20, 20], # bin 3!
55 ... [1, 2, 30, 30, 40, 40], # bin 2!
56 ... [1, 1, 20, 20, 30, 30]], int), tempa)
57 7
58 """
59 total_bins: int = -1 # the current idea of what the last bin is
60 temp.fill(0) # empty all temporary values
61 n_items: Final[int] = len(y) # the number of rows in the matrix
63 for i in range(n_items): # iterate over all packed items
64 bin_idx: int = int(y[i, IDX_BIN]) - 1 # get the bin index of the item
65 temp[bin_idx] += 1 # increase number of items
66 total_bins = max(total_bins, bin_idx)
67 return (n_items * total_bins) + temp[0:total_bins + 1].min()
70class BinCountAndEmpty(BinCountAndLastEmpty):
71 """Get the number of bins and number of elements in the emptiest one."""
73 def __init__(self, instance: Instance) -> None:
74 """
75 Initialize the objective function.
77 :param instance: the instance to load the bounds from
78 """
79 super().__init__(instance)
80 #: the internal temporary array
81 self.__temp: Final[np.ndarray] = np.empty(
82 instance.n_items, instance.dtype)
84 def evaluate(self, x) -> int:
85 """
86 Evaluate the objective function.
88 :param x: the solution
89 :return: the bin size and smallest-bin-area factor
90 """
91 return bin_count_and_empty(x, self.__temp)
93 def __str__(self) -> str:
94 """
95 Get the name of the bins objective function.
97 :return: `binCountAndEmpty`
98 :retval "binCountAndEmpty": always
99 """
100 return "binCountAndEmpty"