@@ -161,8 +161,67 @@ def search(self, coalition: np.ndarray) -> float:
161161 )
162162
163163 # predict performance values with the help of the surrogate model for the filtered configurations
164- vals : np .ndarray = np .array (self .explanation_task .get_single_surrogate_model ().evaluate (filtered_samples ))
164+ if len (filtered_samples ) > 0 :
165+ vals : np .ndarray = np .array (
166+ self .explanation_task .get_single_surrogate_model ().evaluate (filtered_samples ),
167+ )
168+ else :
169+ logger .warning (
170+ "WARNING: After filtering for conditions, no configurations were left, thus, using baseline value." ,
171+ )
172+ vals = np .array ([self .search (np .array ([False ] * len (coalition )))])
165173 else :
166174 vals : np .ndarray = np .array (self .explanation_task .get_single_surrogate_model ().evaluate (temp_random_sample ))
167175
168- return evaluate_aggregation (self .mode , vals )
176+ # determine the final, aggregated value of the coalition
177+ res = evaluate_aggregation (self .mode , vals )
178+
179+ # in case we are maximizing or minimizing, ensure that the value function is monotone
180+ if self .mode in (Aggregation .MAX , Aggregation .MIN ):
181+ res = self ._ensure_monotonicity (coalition , res )
182+
183+ # cache the coalition's value
184+ self .coalition_cache [str (coalition .tolist ())] = res
185+
186+ return res
187+
188+ def _ensure_monotonicity (self , coalition : np .ndarray , value : float ) -> float :
189+ """Ensure that the value function is monotonically increasing/decreasing depending on whether we want to maximize or minimize respectively.
190+
191+ Args:
192+ coalition: The current coalition.
193+ value: The value of the coalition as determined by searching.
194+
195+ Returns: The monotonicity-ensured value of the coalition.
196+
197+ """
198+ monotone_value = value
199+ checked_one = False
200+
201+ for i in range (len (coalition )):
202+ if coalition [i ]: # check whether the entry is True and set it to False to check for a cached result
203+ temp_coalition = coalition .copy ()
204+ temp_coalition [i ] = False
205+ if str (temp_coalition .tolist ()) in self .coalition_cache :
206+ checked_one = True
207+ monotone_value = evaluate_aggregation (
208+ self .mode ,
209+ np .array ([
210+ monotone_value ,
211+ self .coalition_cache [str (temp_coalition .tolist ())],
212+ ]),
213+ )
214+
215+ if not checked_one and coalition .any ():
216+ logger .warning (
217+ "Could not ensure monotonicity as none of the coalitions with one player less has been cached so far." ,
218+ )
219+
220+ if value < monotone_value : # pragma: no cover
221+ logger .debug (
222+ "Ensured monotonicity with a sub-coalition's value. Increased the value of the current coalition from %s to %s." ,
223+ value ,
224+ monotone_value ,
225+ )
226+
227+ return monotone_value
0 commit comments