44from abc import ABC , abstractmethod
55from ac_functionalities .config_gens import DefaultConfigGen , RandomConfigGen
66from ac_functionalities .ranking import trueskill
7- from ac_functionalities .rtac_data import RTACData , Configuration
7+ from ac_functionalities .rtac_data import (
8+ RTACData ,
9+ Configuration ,
10+ ACMethod ,
11+ InterimMeaning
12+ )
813from ac_functionalities .logs import RTACLogs
914from multiprocessing import Value
1015import argparse
1318import numpy as np
1419import uuid
1520import copy
21+ from scipy .stats import rankdata
1622
1723
1824class contender (object ):
@@ -42,7 +48,6 @@ def __init__(self, scenario: argparse.Namespace, logs: RTACLogs) -> None:
4248 @abstractmethod
4349 def init_data (self ) -> None :
4450 """Initialize tournament result processing data."""
45- ...
4651
4752 @abstractmethod
4853 def process_results (self , rtac_data : RTACData ) -> None :
@@ -53,17 +58,14 @@ def process_results(self, rtac_data: RTACData) -> None:
5358 throughout the rtac modules.
5459 :type rtac_data: RTACData
5560 """
56- ...
5761
5862 @abstractmethod
5963 def manage_pool (self ) -> None :
6064 """Replace contenders in pool if necessary."""
61- ...
6265
6366 @abstractmethod
6467 def select_contenders (self ) -> None :
6568 """Select contenders for next tournament/problem instance."""
66- ...
6769
6870 @abstractmethod
6971 def process_tourn (self , rtac_data : RTACData ) -> Value :
@@ -74,7 +76,6 @@ def process_tourn(self, rtac_data: RTACData) -> Value:
7476 throughout the rtac modules.
7577 :type rtac_data: RTACData
7678 """
77- ...
7879
7980 def get_contender_dict (self ) -> dict [str : Configuration ]:
8081 """Returns contender_dict.
@@ -88,6 +89,7 @@ def get_contender_dict(self) -> dict[str: Configuration]:
8889
8990
9091class ResultProcessing (AbstractResultProcessing ):
92+ """Process results of prvious tournament."""
9193
9294 def __init__ (self , scenario : argparse .Namespace , logs : RTACLogs ) -> None :
9395 """Initialize tournament result processing class as necessary for
@@ -159,6 +161,32 @@ def init_data(self) -> dict[str: Configuration]:
159161
160162 return self .contender_dict
161163
164+ def get_winner (self , times : list [float ], res : list [float ]) \
165+ -> tuple [int , list [int ]]:
166+ """Get index of the winning configuration and ranks.
167+
168+ :param times: List of time results of previous tournament.
169+ :type times: list[float]
170+ :param res: List of objective results of previous tournament.
171+ :type res: list[float]
172+ :returns: Index of winner and ranks.
173+ :rtype: tuple[int, list[int]]
174+ """
175+ if not self .scenario .objective_min :
176+ winner = times .index (min (times ))
177+
178+ else :
179+ winner = res .index (min (res ))
180+
181+ ranks = []
182+ for core in range (self .scenario .number_cores ):
183+ if core == winner :
184+ ranks .append (1 )
185+ else :
186+ ranks .append (2 )
187+
188+ return winner , ranks
189+
162190 def process_results (self , rtac_data : RTACData ) -> None :
163191 """Perform tournament result processing necessary to replace contenders
164192 in pool and select contenders for next tournament/problem instance
@@ -179,11 +207,7 @@ def process_results(self, rtac_data: RTACData) -> None:
179207 res = list (self .rtac_data .ta_res [:])
180208 self .rtac_data .best_res = min (res )
181209
182- if not self .scenario .objective_min :
183- winner = times .index (min (times ))
184-
185- else :
186- winner = res .index (min (res ))
210+ winner , ranks = self .get_winner (times , res )
187211
188212 if self .scenario .verbosity in (1 , 2 ):
189213 res = list (self .rtac_data .ta_res [:])
@@ -202,19 +226,13 @@ def process_results(self, rtac_data: RTACData) -> None:
202226
203227 # Set the results of the tournament
204228 individuals = [None ] * self .scenario .number_cores
229+ contender_ids = list (self .contender_dict .keys ())
205230 for core in range (self .scenario .number_cores ):
206- contender_ids = list (self .contender_dict .keys ())
207- rank = (self .scores [contender_ids [core ]][0 ],
208- self .scores [contender_ids [core ]][1 ])
209- individuals [core ] = contender_ids [core ]
231+ skill = (self .scores [contender_ids [core ]][0 ],
232+ self .scores [contender_ids [core ]][1 ])
210233 individuals [core ] = contender ()
211- individuals [core ].skill = rank
212-
213- for core in range (self .scenario .number_cores ):
214- if core == winner :
215- individuals [core ].rank = 1
216- else :
217- individuals [core ].rank = 2
234+ individuals [core ].skill = skill
235+ individuals [core ].rank = ranks [core ]
218236
219237 if self .scenario .verbosity == 2 :
220238 print ('Contender' , contender_ids [core ], 'has the rank' ,
@@ -373,8 +391,130 @@ def process_tourn(self, rtac_data: RTACData) -> str:
373391 self .rtac_data .newtime = self .rtac_data .ta_res_time [0 ]
374392
375393 if self .scenario .verbosity in (1 , 2 ):
394+ if self .rtac_data .winner .value == 0 :
395+ winner = None
396+ else :
397+ winner = self .rtac_data .winner .value
376398 print ('\n \n ' )
377- print ('Winner was' , self . rtac_data . winner . value )
399+ print ('Winner was' , winner )
378400 print ('\n \n ' )
379401
380402 return self .rtac_data .winner .value
403+
404+
405+ class ResultProcessingpp (ResultProcessing ):
406+ """Process results of prvious tournament."""
407+
408+ def duplicates (self , ranks : list [int ], rank : float ) -> list [int ]:
409+ """List the indices of the result in the results list.
410+
411+ :param ranks: List of objective results of previous tournament.
412+ :type ranks: list[int]
413+ :param rank: a single result.
414+ :type rank: float
415+ :returns: list of indices of this result in the results list.
416+ :rtype: list[int]
417+ """
418+ return [i for i , x in enumerate (ranks ) if x == rank ]
419+
420+ def get_winner (self , times : list [float ], res : list [float ]) \
421+ -> tuple [int , list [int ]]:
422+ """Get index of the winning configuration including last known
423+ intermidiate outputs to break ties. Additionally output complete
424+ ranking to compute more detailed assessment with trueskill.
425+
426+ :param times: List of time results of previous tournament.
427+ :type times: list[float]
428+ :param res: List of objective results of previous tournament.
429+ :type res: list[float]
430+ :returns: Index of winner and ranks.
431+ :rtype: tuple[int, list[int]]
432+ """
433+ if not self .scenario .objective_min :
434+ winner = times .index (min (times ))
435+
436+ else :
437+ winner = res .index (min (res ))
438+
439+ interim_sorted = [[self .rtac_data .interim [j ][i ]
440+ for j in range (self .scenario .number_cores )]
441+ for i , _ in enumerate (self .rtac_data .interim [0 ])]
442+
443+ interim_sorted = np .array (interim_sorted )
444+ interim_sorted = interim_sorted .astype (float )
445+
446+ for i , isort in enumerate (interim_sorted ):
447+ if self .rtac_data .interim_meaning [i ] is \
448+ InterimMeaning .decrease :
449+ interim_sorted [i ] = rankdata (isort ,
450+ method = 'dense' ,
451+ nan_policy = "propagate" )
452+ elif self .rtac_data .interim_meaning [i ] is \
453+ InterimMeaning .increase :
454+ interim_sorted [i ] = rankdata ([- 1 * i if i is not None
455+ else None for i in isort ],
456+ method = 'dense' ,
457+ nan_policy = "propagate" )
458+
459+ ranks = [0 for core in range (self .scenario .number_cores )]
460+
461+ for _ , isort in enumerate (interim_sorted ):
462+ for r , _ in enumerate (ranks ):
463+ ranks [r ] += isort [r ]
464+
465+ if self .scenario .objective_min :
466+ res_ranks = rankdata (res , method = 'dense' , nan_policy = "propagate" )
467+
468+ duplicates = []
469+
470+ for rank in sorted (set (res_ranks )):
471+ duplicates .append (self .duplicates (res_ranks , rank ))
472+
473+ for duplicate in duplicates :
474+ if len (duplicate ) > 1 :
475+ interim_ranks = [ranks [dup ] for dup in duplicate ]
476+ tie_winner = \
477+ duplicate [interim_ranks .index (min (interim_ranks ))]
478+ interim_ranks = rankdata (interim_ranks , method = 'dense' )
479+ interim_ranks -= min (interim_ranks )
480+ for d , ir in zip (duplicate , interim_ranks ):
481+ for r , _ in enumerate (res_ranks ):
482+ if d == r and r != winner and r != tie_winner :
483+ res_ranks [r ] += interim_ranks [ir ]
484+ else :
485+ res_ranks [r ] += max (interim_ranks )
486+
487+ ranks = res_ranks
488+
489+ ranks = rankdata (ranks , method = 'dense' )
490+
491+ ranks [winner ] = 0
492+
493+ return winner , ranks
494+
495+
496+ def processing_factory (scenario , logs ) -> AbstractResultProcessing :
497+ """Class factory to return the initialized class with data structures
498+ appropriate to the RTAC method scenario.ac.
499+
500+ :param scenario: Namespace containing all settings for the RTAC.
501+ :type scenario: argparse.Namespace
502+ :returns: Inititialized BaseTARunner object matching the RTAC method of
503+ the scenario.
504+ :rtype: BaseTARunner
505+ """
506+ if scenario .ac in (ACMethod .ReACTR , ACMethod .CPPL ):
507+ return ResultProcessing (scenario , logs )
508+
509+ elif scenario .ac == ACMethod .ReACTRpp :
510+ return ResultProcessingpp (scenario , logs )
511+
512+ '''
513+
514+ elif scenario.ac == ACMethod.GRAYBOX:
515+ return GBTARunner(scenario)
516+ '''
517+
518+
519+ if __name__ == "__main__" :
520+ pass
0 commit comments