Coverage for moptipyapps / spoc / spoc_4 / challenge_1 / beginner / objective_with_penalty.py: 43%

44 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-03 04:37 +0000

1""" 

2The objective function of the beginner problem. 

3 

4>>> inst = Instance("matching-i") 

5>>> obj = BeginnerObjectiveP(inst) 

6 

7>>> test_x = np.ndarray(inst.n, dtype=np.bool) 

8>>> test_x.fill(0) 

9 

10>>> obj.evaluate(test_x) 

110 

12 

13>>> test_x[0] = 1 

14>>> obj.evaluate(test_x) 

15-267 

16>>> inst[0, -1] 

17np.int64(267) 

18 

19The clash of two orbits incurs a penalty: 

20 

21>>> test_x[2234] = 1 

22>>> obj.evaluate(test_x) 

23125518718 

24 

25>>> inst.penalty - 267 - 7636 

26125518718 

27""" 

28 

29from typing import Final 

30 

31import numba # type: ignore 

32import numpy as np 

33from moptipy.api.objective import Objective 

34from moptipy.utils.logger import KeyValueLogSection 

35 

36from moptipyapps.spoc.spoc_4.challenge_1.beginner.base_obj import ( 

37 BaseObjectWithArrays, 

38) 

39from moptipyapps.spoc.spoc_4.challenge_1.beginner.instance import Instance 

40 

41 

42@numba.njit(cache=True, inline="always", fastmath=False, boundscheck=False) 

43def _compute(x: np.ndarray, data: np.ndarray, penalty: int, 

44 earth: np.ndarray, lunar: np.ndarray, dest: np.ndarray) -> int: 

45 """ 

46 Compute the objective function of the beginner problem. 

47 

48 :param x: the candidate solution 

49 :param data: the orbit data 

50 :param penalty: the penalty for clashes 

51 :param earth: the earth orbits 

52 :param lunar: the lunar orbits 

53 :param dest: the destination orbits 

54 :return: the objective value, minus the offset 

55 """ 

56 earth.fill(0) 

57 lunar.fill(0) 

58 dest.fill(0) 

59 result: int = 0 

60 for i, use in enumerate(x): 

61 if not use: 

62 continue 

63 ue, ul, ud, uo = data[i, :] 

64 if earth[ue]: 

65 result += penalty 

66 else: 

67 earth[ue] = 1 

68 if lunar[ul]: 

69 result += penalty 

70 else: 

71 lunar[ul] = 1 

72 if dest[ud]: 

73 result += penalty 

74 else: 

75 dest[ud] = 1 

76 result -= uo 

77 return int(result) 

78 

79 

80class BeginnerObjectiveP(BaseObjectWithArrays, Objective): 

81 """The objective function of the beginner problem.""" 

82 

83 def __init__(self, instance: Instance) -> None: 

84 """ 

85 Create the objective function of the beginner problem. 

86 

87 :param instance: the instance of the objective function. 

88 """ 

89 super().__init__(instance) 

90 #: the penalty 

91 self.__p: Final[int] = instance.penalty 

92 

93 def evaluate(self, x) -> int: 

94 """ 

95 Evaluate the objective function of the beginner problem. 

96 

97 :param x: the solution vector 

98 :return: the result 

99 """ 

100 return _compute(x, self.instance, self.__p, self.earth, 

101 self.lunar, self.dest) 

102 

103 def lower_bound(self) -> int: 

104 """ 

105 Get the lower bound. 

106 

107 :return: the lower bound 

108 """ 

109 return 1 - self.__p 

110 

111 def upper_bound(self) -> int: 

112 """ 

113 Get the upper bound. 

114 

115 :return: the upper bound 

116 """ 

117 return self.instance.n * 3 * self.__p 

118 

119 def __str__(self) -> str: 

120 """ 

121 Get the name of the objective function. 

122 

123 :return: the name of the objective function 

124 """ 

125 return "spoc4beginnerP" 

126 

127 def log_parameters_to(self, logger: KeyValueLogSection) -> None: 

128 """ 

129 Log all parameters of this component as key-value pairs. 

130 

131 :param logger: the logger for the parameters 

132 """ 

133 super().log_parameters_to(logger) 

134 with logger.scope("i") as inst: 

135 self.instance.log_parameters_to(inst)