22instances/tournaments as well as stats about toournaments and results."""
33
44from abc import ABC , abstractmethod
5- from typing import Any
5+ from typing import Any , Optional
66import argparse
77import os
88import logging
9+ import joblib
10+ import numpy as np
11+ import copy
912from logging .handlers import RotatingFileHandler , BaseRotatingHandler
1013from ac_functionalities .rtac_data import (
1114 Configuration ,
1215 RTACData ,
13- TournamentStats
16+ TournamentStats ,
17+ ACMethod ,
18+ Generator
1419)
15- __all__ = ('Configuration' , )
20+ __all__ = ('Configuration' )
1621
1722
1823class NewRotatingFileHandler (RotatingFileHandler ):
@@ -36,6 +41,8 @@ def __init__(self, scenario: argparse.Namespace):
3641 :param scenario: Namespace containing all settings for the RTAC.
3742 :type scenario: argparse.Namespace
3843 """
44+ self .scenario = scenario
45+ self .ranking = scenario .ac
3946 if not os .path .isdir (scenario .log_folder ):
4047 os .makedirs (scenario .log_folder )
4148 self .log_path = scenario .log_folder + '/' \
@@ -183,34 +190,51 @@ class RTACLogs(AbstractLogs):
183190
184191 def init_ranking_logs (self ) -> None :
185192 """Initializes loggers for data concerning ReACTR."""
193+
186194 # Set up pool logging
187195 self .pool_log = logging .getLogger ('pool_log' )
188196 self .pool_log .setLevel (logging .INFO )
189197 p_fh = logging .FileHandler (f'{ self .log_path } /pool_tourn_0.log' )
190198 p_fh .setLevel (logging .INFO )
191199 self .pool_log .addHandler (p_fh )
192200
193- # Set up trueskill scores logging
194- self .scores_log = logging .getLogger ('scores_log' )
195- self .scores_log .setLevel (logging .INFO )
196- s_fh = logging .FileHandler (f'{ self .log_path } /scores_tourn_0.log' )
197- s_fh .setLevel (logging .INFO )
198- self .scores_log .addHandler (s_fh )
201+ if self .ranking in (ACMethod .ReACTR , ACMethod .ReACTRpp ):
202+ # Set up trueskill scores logging
203+ self .scores_log = logging .getLogger ('scores_log' )
204+ self .scores_log .setLevel (logging .INFO )
205+ s_fh = logging .FileHandler (f'{ self .log_path } /scores_tourn_0.log' )
206+ s_fh .setLevel (logging .INFO )
207+ self .scores_log .addHandler (s_fh )
208+ elif self .ranking is ACMethod .CPPL :
209+ # Set up bandit logging
210+ self .bandit_log = logging .getLogger ('bandit_log' )
211+ self .bandit_log .setLevel (logging .INFO )
212+ b_fh = logging .FileHandler (f'{ self .log_path } /bandit_tourn_0.log' )
213+ b_fh .setLevel (logging .INFO )
214+ self .bandit_log .addHandler (b_fh )
199215
200216 def ranking_log (self , pool : dict [str : Configuration ],
201- scores : dict [str : tuple [int , int ]], tourn_nr : int ,
202- contender_dict : dict [str : Configuration ]) -> None :
217+ assessment : dict [str : Any ], tourn_nr : int ,
218+ contender_dict : dict [str : Configuration ],
219+ ** kwargs ) -> None :
203220 """Logs data concerning ReACTR.
204221
205222 :param pool: Dictionary with configuration id as key and configuration
206223 as value with scenario.contenders == #items .
207224 :type pool: dict
208- :param scores: Dictionary with configuration id as key and tuple of Mu
209- and Sigma as trueskill performance assessments as value.
210- :type scores: dict
225+ :param assessment: Dictionary with configuration id as key and
226+ assessment depending on the AC method used, e.g., trueskill scores,
227+ or bandit model.
228+ :type assessment: dict
211229 :param contender_dict: Dictionary with configuration id as key and
212230 configuration as value: contenders of the previous tournament.
213231 :type contender_dict: dict
232+ **kwargs : dict, optional
233+ Additional keyword arguments. Possible keys include:
234+
235+ - `standard_scaler` sklearn
236+ - `min_max_scaler` sklearn
237+ - `pca_obj_params` sklearn
214238 """
215239 self .contender_dict_log .handlers .clear ()
216240 cl_fh = logging .FileHandler (
@@ -224,18 +248,67 @@ def ranking_log(self, pool: dict[str: Configuration],
224248 f'{ self .log_path } /pool_tourn_{ tourn_nr } .log' )
225249 p_fh .setLevel (logging .INFO )
226250 self .pool_log .addHandler (p_fh )
227- self .pool_log .info (str (pool ))
228251
229- self .scores_log .handlers .clear ()
230- s_fh = logging .FileHandler (
231- f'{ self .log_path } /scores_tourn_{ tourn_nr } .log' )
232- s_fh .setLevel (logging .INFO )
233- self .scores_log .addHandler (s_fh )
234- self .scores_log .info (str (scores ))
252+ serializable_pool = copy .deepcopy (pool )
253+ for conf in serializable_pool .values ():
254+ conf .gen = conf .gen .name
255+ self .pool_log .info (str (serializable_pool ))
256+
257+ if self .ranking in (ACMethod .ReACTR , ACMethod .ReACTRpp ):
258+ self .scores_log .handlers .clear ()
259+ s_fh = logging .FileHandler (
260+ f'{ self .log_path } /scores_tourn_{ tourn_nr } .log' )
261+ s_fh .setLevel (logging .INFO )
262+ self .scores_log .addHandler (s_fh )
263+ self .scores_log .info (str (assessment ))
264+ elif self .ranking is ACMethod .CPPL :
265+ if not os .path .isdir (f'{ self .log_path } /bandit_models' ):
266+ os .mkdir (f'{ self .log_path } /bandit_models' )
267+ self .bm_path = f'{ self .log_path } /bandit_models'
268+ bandit_models = kwargs ['bandit_models' ]
269+ joblib .dump (
270+ bandit_models ['standard_scaler' ],
271+ f'{ self .bm_path } /standard_scaler_{ tourn_nr } .pkl' )
272+ joblib .dump (
273+ bandit_models ['min_max_scaler' ],
274+ f'{ self .bm_path } /min_max_scaler_{ tourn_nr } .pkl' )
275+ joblib .dump (
276+ bandit_models ['one_hot_encoder' ],
277+ f'{ self .bm_path } /one_hot_encoder_{ tourn_nr } .pkl' )
278+ joblib .dump (
279+ bandit_models ['pca_obj_params' ],
280+ f'{ self .bm_path } /pca_obj_params_{ tourn_nr } .pkl' )
281+ joblib .dump (
282+ bandit_models ['pca_obj_inst' ],
283+ f'{ self .bm_path } /pca_obj_inst_{ tourn_nr } .pkl' )
284+ self .bandit_log .handlers .clear ()
285+ b_fh = logging .FileHandler (
286+ f'{ self .log_path } /bandit_tourn_{ tourn_nr } .log' )
287+ b_fh .setLevel (logging .INFO )
288+ self .bandit_log .addHandler (b_fh )
289+ self .bandit_log .info (str (assessment ))
290+
291+ def parse_array (self , val ):
292+ if not isinstance (val , str ):
293+ return val
294+
295+ val = val .strip ()
296+
297+ # Array-like: [1. 2. 3.]
298+ if val .startswith ('[' ) and val .endswith (']' ):
299+ val = val .strip ("[]\n " )
300+ return np .fromstring (val , sep = ' ' )
301+
302+ # Scalar string: try to convert to int or float
303+ try :
304+ return int (val ) if '.' not in val else float (val )
305+ except ValueError :
306+ raise ValueError (f"Cannot parse value: { val } " )
235307
236308 def load_data (self , tourn_nr : int | None = None ) \
237- -> tuple [dict [str : Configuration ], dict [str : tuple [int , int ]],
238- dict [str : Configuration ], int ]:
309+ -> tuple [dict [str : Configuration ], dict [str : Any ],
310+ dict [str : Configuration ], int ,
311+ Optional [Any ]]:
239312 """Loads data necessary for resuming the algorithm configuration from
240313 last logged state of ReACTR.
241314
@@ -249,22 +322,64 @@ def load_data(self, tourn_nr: int | None = None) \
249322 tourn_nr = int (f .readline ().strip ())
250323
251324 with open (f'{ self .log_path } /pool_tourn_{ tourn_nr } .log' , 'r' ) as f :
252- pool = eval (f .readline ())
253-
254- with open (f'{ self .log_path } /scores_tourn_{ tourn_nr } .log' , 'r' ) as f :
255- scores = eval (f .readline ())
325+ line = f .readline ()
326+ pool = eval (line )
327+ for conf in pool .values ():
328+ conf .gen = Generator [conf .gen ]
329+
330+ if self .ranking in (ACMethod .ReACTR , ACMethod .ReACTRpp ):
331+ with open (
332+ f'{ self .log_path } /scores_tourn_{ tourn_nr } .log' , 'r' ) as f :
333+ assessment = eval (f .readline ())
334+ if self .scenario .experimental :
335+ assessment = dict (zip (list (pool .keys ()), assessment .values ()))
336+ elif self .ranking is ACMethod .CPPL :
337+ self .bm_path = f'{ self .log_path } /bandit_models'
338+ with open (
339+ f'{ self .log_path } /bandit_tourn_{ tourn_nr } .log' , 'r' ) as f :
340+ assessment = f .read ()
341+ #assessment = ast.literal_eval(assessment)
342+ assessment = eval (assessment , {"array" : np .array })
343+ assessment = \
344+ {k : self .parse_array (v ) for k , v in assessment .items ()}
345+ standard_scaler = \
346+ joblib .load (f'{ self .bm_path } /standard_scaler_{ tourn_nr } .pkl' )
347+ min_max_scaler = \
348+ joblib .load (f'{ self .bm_path } /min_max_scaler_{ tourn_nr } .pkl' )
349+ one_hot_encoder = \
350+ joblib .load (f'{ self .bm_path } /one_hot_encoder_{ tourn_nr } .pkl' )
351+ pca_obj_params = \
352+ joblib .load (f'{ self .bm_path } /pca_obj_params_{ tourn_nr } .pkl' )
353+ pca_obj_inst = \
354+ joblib .load (f'{ self .bm_path } /pca_obj_inst_{ tourn_nr } .pkl' )
355+ bandit_models = {'standard_scaler' : standard_scaler ,
356+ 'min_max_scaler' : min_max_scaler ,
357+ 'one_hot_encoder' : one_hot_encoder ,
358+ 'pca_obj_params' : pca_obj_params ,
359+ 'pca_obj_inst' : pca_obj_inst }
256360
257361 with open (f'{ self .log_path } /contender_dict_tourn_{ tourn_nr } .log' ,
258362 'r' ) as f :
259363 contender_ids = eval (f .readline ())
260364
261365 if self .experimental :
366+ if self .ranking in (ACMethod .ReACTR , ACMethod .ReACTRpp ):
367+ os .remove (f'{ self .log_path } /scores_tourn_{ tourn_nr } .log' )
368+ elif self .ranking is ACMethod .CPPL :
369+ os .remove (f'{ self .log_path } /bandit_tourn_{ tourn_nr } .log' )
370+ os .remove (f'{ self .bm_path } /standard_scaler_{ tourn_nr } .pkl' )
371+ os .remove (f'{ self .bm_path } /min_max_scaler_{ tourn_nr } .pkl' )
372+ os .remove (f'{ self .bm_path } /one_hot_encoder_{ tourn_nr } .pkl' )
373+ os .remove (f'{ self .bm_path } /pca_obj_params_{ tourn_nr } .pkl' )
374+ os .remove (f'{ self .bm_path } /pca_obj_inst_{ tourn_nr } .pkl' )
262375 os .remove (f'{ self .log_path } /pool_tourn_{ tourn_nr } .log' )
263- os .remove (f'{ self .log_path } /scores_tourn_{ tourn_nr } .log' )
264376 os .remove (f'{ self .log_path } /contender_dict_tourn_{ tourn_nr } .log' )
265377
266378 contender_dict = {}
267379 for ci in contender_ids :
268380 contender_dict [ci ] = pool [ci ]
269381
270- return pool , scores , contender_dict , tourn_nr
382+ if self .ranking in (ACMethod .ReACTR , ACMethod .ReACTRpp ):
383+ return pool , assessment , contender_dict , tourn_nr
384+ elif self .ranking is ACMethod .CPPL :
385+ return pool , assessment , contender_dict , tourn_nr , bandit_models
0 commit comments