Coverage for moptipyapps/prodsched/objectives/worst_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-case service level.
4This objective function tries to find solutions which have very robust fill
5rates.
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.
25So we use this as result of our objective function.
26"""
29from moptipy.api.objective import Objective
31from moptipyapps.prodsched.multistatistics import MultiStatistics
34class WorstFillRate(Objective):
35 """Compute the worst service level and return `1 -` of it."""
37 def evaluate(self, x: MultiStatistics) -> int | float:
38 """
39 Get the negated worst service level.
41 :param x: the multi-statistics
42 :return: the worst stock level
43 """
44 min_imm: int | float = 1
45 for stat in x.per_instance:
46 for sl in stat.service_levels:
47 if (sl is None) or not (0 < sl <= 1):
48 return 1
49 min_imm = min(min_imm, sl)
50 return 1 - min_imm
52 def lower_bound(self) -> int:
53 """
54 Get the lower bound of the inverted minimum service level.
56 :retval 0: always
57 """
58 return 0
60 def upper_bound(self) -> int:
61 """
62 Get the upper bound of the inverted minimum service level.
64 :retval 1: always
65 """
66 return 1
68 def __str__(self) -> str:
69 """
70 Get the name of the objective function.
72 :return: `worstFillRate`
73 :retval "worstFillRate": always
74 """
75 return "worstFillRate"