Coverage for moptipyapps / binpacking2d / experiment.py: 50%

46 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-11 04:40 +0000

1"""An example experiment for bin packing.""" 

2 

3import argparse 

4from typing import Callable, Final, cast 

5 

6from moptipy.algorithms.so.ffa.fea1plus1 import FEA1plus1 

7from moptipy.algorithms.so.rls import RLS 

8from moptipy.api.encoding import Encoding 

9from moptipy.api.execution import Execution 

10from moptipy.api.experiment import run_experiment 

11from moptipy.api.objective import Objective 

12from moptipy.operators.signed_permutations.op0_shuffle_and_flip import ( 

13 Op0ShuffleAndFlip, 

14) 

15from moptipy.operators.signed_permutations.op1_swap_2_or_flip import ( 

16 Op1Swap2OrFlip, 

17) 

18from moptipy.spaces.signed_permutations import SignedPermutations 

19from pycommons.io.path import Path 

20 

21from moptipyapps.binpacking2d.encodings.ibl_encoding_1 import ( 

22 ImprovedBottomLeftEncoding1, 

23) 

24from moptipyapps.binpacking2d.instance import Instance 

25from moptipyapps.binpacking2d.packing_result import DEFAULT_OBJECTIVES 

26from moptipyapps.binpacking2d.packing_space import PackingSpace 

27from moptipyapps.utils.shared import moptipyapps_argparser 

28 

29#: the maximum number of FEs 

30MAX_FES: Final[int] = 1_000_000 

31 

32 

33def base_setup(instance: Instance, 

34 encoding: Callable[[Instance], Encoding], 

35 objective: Callable[[Instance], Objective]) \ 

36 -> tuple[SignedPermutations, Execution]: 

37 """ 

38 Create the basic setup. 

39 

40 :param instance: the instance to use 

41 :param encoding: the encoding function 

42 :param objective: the objective function 

43 :return: the search space and the basic execution 

44 """ 

45 space: Final[SignedPermutations] = SignedPermutations( 

46 instance.get_standard_item_sequence()) 

47 

48 return (space, Execution().set_max_fes(MAX_FES).set_log_improvements(True) 

49 .set_objective(objective(instance)) 

50 .set_encoding(encoding(instance)) 

51 .set_search_space(space) 

52 .set_solution_space(PackingSpace(instance))) 

53 

54 

55def rls(instance: Instance, 

56 encoding: Callable[[Instance], Encoding], 

57 objective: Callable[[Instance], Objective]) -> Execution: 

58 """ 

59 Create the RLS setup. 

60 

61 :param instance: the instance to use 

62 :param encoding: the encoding function 

63 :param objective: the objective function 

64 :return: the RLS execution 

65 """ 

66 space, execute = base_setup(instance, encoding, objective) 

67 return execute.set_algorithm( 

68 RLS(Op0ShuffleAndFlip(space), Op1Swap2OrFlip())) 

69 

70 

71def fea(instance: Instance, 

72 encoding: Callable[[Instance], Encoding], 

73 objective: Callable[[Instance], Objective]) -> Execution: 

74 """ 

75 Create the FEA setup. 

76 

77 :param instance: the instance to use 

78 :param encoding: the encoding function 

79 :param objective: the objective function 

80 :return: the RLS execution 

81 """ 

82 space, execute = base_setup(instance, encoding, objective) 

83 return execute.set_algorithm( 

84 FEA1plus1(Op0ShuffleAndFlip(space), Op1Swap2OrFlip())) 

85 

86 

87def run(base_dir: str, n_runs: int = 23) -> None: 

88 """ 

89 Run the experiment. 

90 

91 :param base_dir: the base directory 

92 :param n_runs: the number of runs, by default `23` 

93 """ 

94 use_dir: Final[Path] = Path(base_dir) 

95 use_dir.ensure_dir_exists() 

96 

97 encodings: Final[tuple[Callable[[Instance], Encoding], ...]] = ( 

98 ImprovedBottomLeftEncoding1, 

99 ) 

100 instances: list[str] = [ 

101 inst for inst in Instance.list_resources() 

102 if inst.startswith(("b", "a"))] 

103 inst_creators: list[Callable[[], Instance]] = [cast( 

104 "Callable[[], Instance]", lambda __s=sss: Instance.from_resource(__s)) 

105 for sss in instances] 

106 namer: Final[Instance] = Instance.from_resource(instances[0]) 

107 

108 for objective in DEFAULT_OBJECTIVES: 

109 objective_dir: Path = use_dir.resolve_inside(str(objective(namer))) 

110 objective_dir.ensure_dir_exists() 

111 for encoding in encodings: 

112 encoding_dir: Path = objective_dir.resolve_inside( 

113 str(encoding(namer))) 

114 encoding_dir.ensure_dir_exists() 

115 run_experiment( 

116 base_dir=encoding_dir, 

117 instances=inst_creators, 

118 setups=[ 

119 cast("Callable", 

120 lambda ins, _e=encoding, _o=objective: rls( 

121 ins, _e, _o)), 

122 cast("Callable", 

123 lambda ins, _e=encoding, _o=objective: fea( 

124 ins, _e, _o))], 

125 n_runs=n_runs, 

126 perform_warmup=True, 

127 perform_pre_warmup=True) 

128 

129 

130# Run the experiment from the command line 

131if __name__ == "__main__": 

132 parser: Final[argparse.ArgumentParser] = moptipyapps_argparser( 

133 __file__, "2D Bin Packing", "Run the 2D Bin Packing experiment.") 

134 parser.add_argument( 

135 "dest", help="the directory to store the experimental results under", 

136 type=Path, nargs="?", default="./results/") 

137 args: Final[argparse.Namespace] = parser.parse_args() 

138 run(args.dest)