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
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-12 08:11 +0000
1"""
2Maximize the worst and average service level.
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.
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".
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.
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".
31This objective function combines both concepts, putting special emphasis
32on the worst-case service level.
33It minimizes
35 "100+(1 - worst-case service level) + (1 - average service level)"
36"""
39from moptipy.api.objective import Objective
41from moptipyapps.prodsched.multistatistics import MultiStatistics
44class WorstAndMeanFillRate(Objective):
45 """Combine and minimize worst and average service level."""
47 def evaluate(self, x: MultiStatistics) -> int | float:
48 """
49 Get the negated worst service level combined with the average.
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)
63 def lower_bound(self) -> int:
64 """
65 Get the lower bound of the inverted minimum service level.
67 :retval 0: always
68 """
69 return 0
71 def upper_bound(self) -> int:
72 """
73 Get the upper bound of the inverted minimum service level.
75 :retval 101: always
76 """
77 return 101
79 def __str__(self) -> str:
80 """
81 Get the name of the objective function.
83 :return: `worstMinAndMeanFillRate`
84 :retval "worstMinAndMeanFillRate": always
85 """
86 return "worstAndMeanFillRate"