Skip to content

Commit d5ba91c

Browse files
committed
CPPL implementation
1 parent 3ff8db4 commit d5ba91c

1,685 files changed

Lines changed: 4461 additions & 257 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.idea/.gitignore

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/.name

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/RTAC.iml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/profiles_settings.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
# RTAC
22
Realtime Algorithm Configuration Methods
3+
4+
This Software is a refactored version of the software used in "Pool-Based Realtime Algorithm Configuration" and "Realtime gray-box algorithm configuration using cost-sensitive classification". It has also extended options regarding logging, input, e.g. parameter space via PCS files, and target algorithm calls.

rtac/ac_functionalities/config_gens.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
DiscreteParameter,
1616
ContinuousParameter,
1717
CategoricalParameter,
18-
BinaryParameter
18+
BinaryParameter,
19+
Generator
1920
)
2021

2122

@@ -44,7 +45,7 @@ def __init__(self, scenario: argparse.Namespace):
4445
self.config_space = scenario.config_space
4546
self.default_config = {}
4647

47-
def generate(self) -> Configuration:
48+
def generate(self, tourn) -> Configuration:
4849
"""Generates and stores default configuration.
4950
5051
:returns: Default configuration.
@@ -70,7 +71,10 @@ def generate(self) -> Configuration:
7071
self.scenario.config_space.get_default_configuration())
7172

7273
self.default_config = \
73-
Configuration(uuid.uuid4().hex, default_config, [])
74+
Configuration(
75+
uuid.uuid4().hex, default_config, [],
76+
Generator.default, tourn
77+
)
7478

7579
return self.default_config
7680

@@ -87,7 +91,7 @@ def __init__(self, scenario: argparse.Namespace):
8791
self.scenario = scenario
8892
self.config_space = scenario.config_space
8993

90-
def generate(self) -> Configuration:
94+
def generate(self, tourn) -> Configuration:
9195
"""Generates random configuration.
9296
9397
:returns: Random configuration.
@@ -210,4 +214,6 @@ def generate(self) -> Configuration:
210214
random_config = \
211215
dict(self.scenario.config_space.sample_configuration(1))
212216

213-
return Configuration(uuid.uuid4().hex, random_config, [])
217+
return Configuration(
218+
uuid.uuid4().hex, random_config, [], Generator.random, tourn
219+
)

rtac/ac_functionalities/logs.py

Lines changed: 144 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@
22
instances/tournaments as well as stats about toournaments and results."""
33

44
from abc import ABC, abstractmethod
5-
from typing import Any
5+
from typing import Any, Optional
66
import argparse
77
import os
88
import logging
9+
import joblib
10+
import numpy as np
11+
import copy
912
from logging.handlers import RotatingFileHandler, BaseRotatingHandler
1013
from 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

1823
class 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

Comments
 (0)