Coverage for moptipyapps/prodsched/objectives/worst_and_mean_service_level.py: 41%

17 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-09-12 08:11 +0000

1""" 

2Maximize the worst and average service level. 

3 

4This objective function tries to find solutions which have very robust 

5and also good service levels. 

6The service level is the fraction of customers that can get served directly, 

7i.e., the fraction of customers that do not need to wait. 

8This means that it is the fraction of customers whose demands can directly be 

9satisfied from the stock. 

10 

11Service levels are between 0 and 1. 

12Of course, high service levels are good and should therefore be subject to 

13maximization. 

14However, since we can only *minimize*, we minimize "1 - service level". 

15 

16Now, the question is: 

17What is a *robust* service level / solution? 

18Well, we simulate the solutions (such as re-order points) over multiple 

19instances. 

20A robust good service level would be high on the worst instance. 

21In other words, the smallest service level measured on any instance should 

22be as high as possible. 

23This means that the largest value "1 - service level" should be as small as 

24possible. 

25 

26However, this does not consider the average performance. 

27A good average performance would mean that we maximize the *average* 

28service level over all instances, or, in terms of minimization, that we 

29minimize "1 - average service level". 

30 

31This objective function combines both concepts, putting special emphasis 

32on the worst-case service level. 

33It minimizes 

34 

35 "100+(1 - worst-case service level) + (1 - average service level)" 

36""" 

37 

38 

39from moptipy.api.objective import Objective 

40 

41from moptipyapps.prodsched.multistatistics import MultiStatistics 

42 

43 

44class WorstAndMeanFillRate(Objective): 

45 """Combine and minimize worst and average service level.""" 

46 

47 def evaluate(self, x: MultiStatistics) -> int | float: 

48 """ 

49 Get the negated worst service level combined with the average. 

50 

51 :param x: the multi-statistics 

52 :return: the worst stock level 

53 """ 

54 min_imm: int | float = 1 

55 avg_imm: int | float = 1 

56 for stat in x.per_instance: 

57 for sl in stat.service_levels: 

58 min_imm = 0 if sl is None else min(min_imm, sl) 

59 avg_imm = 0 if stat.service_level is None else min( 

60 avg_imm, stat.service_level) 

61 return (1 - min_imm) * 100 + (1 - avg_imm) 

62 

63 def lower_bound(self) -> int: 

64 """ 

65 Get the lower bound of the inverted minimum service level. 

66 

67 :retval 0: always 

68 """ 

69 return 0 

70 

71 def upper_bound(self) -> int: 

72 """ 

73 Get the upper bound of the inverted minimum service level. 

74 

75 :retval 101: always 

76 """ 

77 return 101 

78 

79 def __str__(self) -> str: 

80 """ 

81 Get the name of the objective function. 

82 

83 :return: `worstMinAndMeanFillRate` 

84 :retval "worstMinAndMeanFillRate": always 

85 """ 

86 return "worstAndMeanFillRate"