Coverage for moptipyapps / prodsched / objectives / worst_and_mean_fill_rate.py: 41%
17 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-13 08:40 +0000
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-13 08:40 +0000
1"""
2Maximize the worst and average immediate rates.
4This objective function tries to find solutions which have very robust
5and also good fill rates.
6The fill rate 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.
11Fill rates are between 0 and 1.
12Of course, high fill rates are good and should therefore be subject to
13maximization.
14However, since we can only *minimize*, we minimize "1 - fill rate".
16Now, the question is:
17What is a *robust* fill rate / solution?
18Well, we simulate the solutions (such as re-order points) over multiple
19instances.
20A robust good fill rate would be high on the worst instance.
21In other words, the smallest fill rate measured on any instance should
22be as high as possible.
23This means that the largest value "1 - fill rate" 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*
28fill rate over all instances, or, in terms of minimization, that we minimize
29"1 - average fill rate".
31This objective function combines both concepts, putting special emphasis
32on the worst-case fill rate.
33It minimizes
35 "100+(1 - worst-case fill rate) + (1 - average fill rate)"
36"""
39from moptipy.api.objective import Objective
41from moptipyapps.prodsched.multistatistics import MultiStatistics
44class WorstAndMeanFillRate(Objective):
45 """Combine and minimize worst and average fill rate."""
47 def evaluate(self, x: MultiStatistics) -> int | float:
48 """
49 Get the negated worst immediate rate.
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.immediate_rates:
58 min_imm = 0 if sl is None else min(min_imm, sl)
59 avg_imm = 0 if stat.immediate_rate is None else min(
60 avg_imm, stat.immediate_rate)
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 immediate rate.
67 :retval 0: always
68 """
69 return 0
71 def upper_bound(self) -> int:
72 """
73 Get the upper bound of the inverted minimum immediate rate.
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"