Coverage for sparkle/platform/settings_objects.py: 72%
1163 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 13:21 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 13:21 +0000
1"""Classes and Enums to control settings."""
2from __future__ import annotations
3import configparser
4from enum import Enum
5import ast
6from pathlib import Path
7from pathlib import PurePath
9from runrunner import Runner
11from sparkle.types import SparkleObjective, resolve_objective
12from sparkle.types.objective import PAR
13from sparkle.configurator.configurator import Configurator
14from sparkle.configurator import implementations as cim
15from sparkle.platform.cli_types import VerbosityLevel
18class SettingState(Enum):
19 """Enum of possible setting states."""
21 NOT_SET = 0
22 DEFAULT = 1
23 FILE = 2
24 CMD_LINE = 3
27class Settings:
28 """Class to read, write, set, and get settings."""
29 # CWD Prefix
30 cwd_prefix = Path() # Empty for now
32 # Library prefix
33 lib_prefix = Path(__file__).parent.parent.resolve()
35 # Default directory names
36 rawdata_dir = Path("Raw_Data")
37 analysis_dir = Path("Analysis")
38 DEFAULT_settings_dir = Path("Settings")
39 __settings_file = Path("sparkle_settings.ini")
40 __latest_settings_file = Path("latest.ini")
42 # Default settings path
43 DEFAULT_settings_path = PurePath(cwd_prefix / DEFAULT_settings_dir / __settings_file)
44 DEFAULT_previous_settings_path =\
45 PurePath(cwd_prefix / DEFAULT_settings_dir / __latest_settings_file)
46 DEFAULT_reference_dir = DEFAULT_settings_dir / "Reference_Lists"
48 # Default library pathing
49 DEFAULT_components = lib_prefix / "Components"
51 # Report Component: Bilbiography
52 bibliography_path = DEFAULT_components / "latex_source" / "report.bib"
54 # Example settings path
55 DEFAULT_example_settings_path = PurePath(DEFAULT_components / "sparkle_settings.ini")
57 # Runsolver component
58 DEFAULT_runsolver_dir = DEFAULT_components / "runsolver" / "src"
59 DEFAULT_runsolver_exec = DEFAULT_runsolver_dir / "runsolver"
61 # Ablation component
62 DEFAULT_ablation_dir = DEFAULT_components / "ablationAnalysis-0.9.4"
63 DEFAULT_ablation_exec = DEFAULT_ablation_dir / "ablationAnalysis"
64 DEFAULT_ablation_validation_exec = DEFAULT_ablation_dir / "ablationValidation"
66 # Default input directory pathing
67 DEFAULT_solver_dir = cwd_prefix / "Solvers"
68 DEFAULT_instance_dir = cwd_prefix / "Instances"
69 DEFAULT_extractor_dir = cwd_prefix / "Extractors"
70 DEFAULT_snapshot_dir = cwd_prefix / "Snapshots"
72 # Default output directory pathing
73 DEFAULT_tmp_output = cwd_prefix / "Tmp"
74 DEFAULT_output = cwd_prefix / "Output"
75 DEFAULT_configuration_output = DEFAULT_output / "Configuration"
76 DEFAULT_selection_output = DEFAULT_output / "Selection"
77 DEFAULT_parallel_portfolio_output = DEFAULT_output / "Parallel_Portfolio"
78 DEFAULT_ablation_output = DEFAULT_output / "Ablation"
79 DEFAULT_log_output = DEFAULT_output / "Log"
81 # Default output subdirs
82 DEFAULT_output_analysis = DEFAULT_output / analysis_dir
84 # Old default output dirs which should be part of something else
85 DEFAULT_feature_data = DEFAULT_output / "Feature_Data"
86 DEFAULT_performance_data = DEFAULT_output / "Performance_Data"
88 # Collection of all working dirs for platform
89 DEFAULT_working_dirs = [
90 DEFAULT_solver_dir, DEFAULT_instance_dir, DEFAULT_extractor_dir,
91 DEFAULT_output, DEFAULT_configuration_output,
92 DEFAULT_selection_output,
93 DEFAULT_output_analysis,
94 DEFAULT_tmp_output, DEFAULT_log_output,
95 DEFAULT_feature_data, DEFAULT_performance_data,
96 DEFAULT_settings_dir, DEFAULT_reference_dir,
97 ]
99 # Old default file paths from GV which should be turned into variables
100 DEFAULT_feature_data_path =\
101 DEFAULT_feature_data / "feature_data.csv"
102 DEFAULT_performance_data_path =\
103 DEFAULT_performance_data / "performance_data.csv"
105 # Constant default values
106 DEFAULT_general_sparkle_objective = PAR(10)
107 DEFAULT_general_sparkle_configurator = cim.SMAC2.__name__
108 DEFAULT_general_solver_cutoff_time = 60
109 DEFAULT_general_extractor_cutoff_time = 60
110 DEFAULT_number_of_jobs_in_parallel = 25
111 DEFAULT_general_verbosity = VerbosityLevel.STANDARD
112 DEFAULT_general_check_interval = 10
113 DEFAULT_general_run_on = "local"
115 DEFAULT_configurator_number_of_runs = 25
116 DEFAULT_configurator_solver_calls = 100
117 DEFAULT_configurator_maximum_iterations = None
119 # Default SMAC2 settings
120 DEFAULT_smac2_wallclock_time = None
121 DEFAULT_smac2_cpu_time = None
122 DEFAULT_smac2_target_cutoff_length = "max"
123 DEFAULT_smac2_use_cpu_time_in_tunertime = None
124 DEFAULT_smac2_cli_cores = None
125 DEFAULT_smac2_max_iterations = None
127 # Default SMAC3 settings
128 DEFAULT_smac3_number_of_runs = None
129 DEFAULT_smac3_facade = "AlgorithmConfigurationFacade"
130 DEFAULT_smac3_facade_max_ratio = None
131 DEFAULT_smac3_crash_cost = None
132 DEFAULT_smac3_termination_cost_threshold = None
133 DEFAULT_smac3_walltime_limit = None
134 DEFAULT_smac3_cputime_limit = None
135 DEFAULT_smac3_use_default_config = None
136 DEFAULT_smac3_min_budget = None
137 DEFAULT_smac3_max_budget = None
139 # Default IRACE settings
140 DEFAULT_irace_max_time = 0 # IRACE equivalent of None in this case
141 DEFAULT_irace_max_experiments = 0
142 DEFAULT_irace_first_test = None
143 DEFAULT_irace_mu = None
144 DEFAULT_irace_max_iterations = None
146 # Default ParamILS settings
147 DEFAULT_paramils_focused_ils = False
148 DEFAULT_paramils_tuner_timeout = None
149 DEFAULT_paramils_focused_approach = None
150 DEFAULT_paramils_min_runs = None
151 DEFAULT_paramils_max_runs = None
152 DEFAULT_paramils_random_restart = None
153 DEFAULT_paramils_initial_configurations = None
154 DEFAULT_paramils_use_cpu_time_in_tunertime = None
155 DEFAULT_paramils_cli_cores = None
156 DEFAULT_paramils_max_iterations = None
158 DEFAULT_slurm_max_parallel_runs_per_node = 8
159 DEFAULT_slurm_job_submission_limit = None
160 DEFAULT_slurm_job_prepend = ""
162 DEFAULT_ablation_racing = False
164 DEFAULT_parallel_portfolio_check_interval = 4
165 DEFAULT_parallel_portfolio_num_seeds_per_solver = 1
167 # Default selection settings
168 DEFAULT_selector_class = "MultiClassClassifier"
169 DEFAULT_selector_model = "RandomForestClassifier"
171 def __init__(self: Settings, file_path: PurePath = None) -> None:
172 """Initialise a settings object."""
173 # Settings 'dictionary' in configparser format
174 self.__settings = configparser.ConfigParser()
176 # Setting flags
177 self.__general_sparkle_objective_set = SettingState.NOT_SET
178 self.__general_sparkle_configurator_set = SettingState.NOT_SET
179 self.__general_solver_cutoff_time_set = SettingState.NOT_SET
180 self.__general_extractor_cutoff_time_set = SettingState.NOT_SET
181 self.__general_verbosity_set = SettingState.NOT_SET
182 self.__general_check_interval_set = SettingState.NOT_SET
184 self.__config_solver_calls_set = SettingState.NOT_SET
185 self.__config_number_of_runs_set = SettingState.NOT_SET
186 self.__config_max_iterations_set = SettingState.NOT_SET
188 self.__smac2_wallclock_time_set = SettingState.NOT_SET
189 self.__smac2_cpu_time_set = SettingState.NOT_SET
190 self.__smac2_use_cpu_time_in_tunertime_set = SettingState.NOT_SET
191 self.__smac2_cli_cores_set = SettingState.NOT_SET
192 self.__smac2_max_iterations_set = SettingState.NOT_SET
193 self.__smac2_target_cutoff_length_set = SettingState.NOT_SET
195 self.__smac3_number_of_trials_set = SettingState.NOT_SET
196 self.__smac3_smac_facade_set = SettingState.NOT_SET
197 self.__smac3_facade_max_ratio_set = SettingState.NOT_SET
198 self.__smac3_crash_cost_set = SettingState.NOT_SET
199 self.__smac3_termination_cost_threshold_set = SettingState.NOT_SET
200 self.__smac3_walltime_limit_set = SettingState.NOT_SET
201 self.__smac3_cputime_limit_set = SettingState.NOT_SET
202 self.__smac3_use_default_config_set = SettingState.NOT_SET
203 self.__smac3_min_budget_set = SettingState.NOT_SET
204 self.__smac3_max_budget_set = SettingState.NOT_SET
206 self.__irace_max_time_set = SettingState.NOT_SET
207 self.__irace_max_experiments_set = SettingState.NOT_SET
208 self.__irace_first_test_set = SettingState.NOT_SET
209 self.__irace_mu_set = SettingState.NOT_SET
210 self.__irace_max_iterations_set = SettingState.NOT_SET
212 self.__paramils_min_runs_set = SettingState.NOT_SET
213 self.__paramils_max_runs_set = SettingState.NOT_SET
214 self.__paramils_tuner_timeout_set = SettingState.NOT_SET
215 self.__paramils_focused_approach_set = SettingState.NOT_SET
216 self.__paramils_random_restart_set = SettingState.NOT_SET
217 self.__paramils_initial_configurations_set = SettingState.NOT_SET
218 self.__paramils_use_cpu_time_in_tunertime_set = SettingState.NOT_SET
219 self.__paramils_cli_cores_set = SettingState.NOT_SET
220 self.__paramils_max_iterations_set = SettingState.NOT_SET
222 self.__run_on_set = SettingState.NOT_SET
223 self.__number_of_jobs_in_parallel_set = SettingState.NOT_SET
224 self.__ablation_racing_flag_set = SettingState.NOT_SET
226 self.__parallel_portfolio_check_interval_set = SettingState.NOT_SET
227 self.__parallel_portfolio_num_seeds_per_solver_set = SettingState.NOT_SET
229 self.__selection_model_set = SettingState.NOT_SET
230 self.__selection_class_set = SettingState.NOT_SET
232 self.__slurm_max_parallel_runs_per_node_set = SettingState.NOT_SET
233 self.__slurm_job_prepend_set = SettingState.NOT_SET
234 self.__slurm_job_submission_limit_set = SettingState.NOT_SET
236 self.__general_sparkle_configurator = None
238 self.__slurm_extra_options_set = dict()
240 if file_path is None:
241 # Initialise settings from default file path
242 self.read_settings_ini()
243 else:
244 # Initialise settings from a given file path
245 self.read_settings_ini(file_path)
247 def read_settings_ini(self: Settings, file_path: PurePath = DEFAULT_settings_path,
248 state: SettingState = SettingState.FILE) -> None:
249 """Read the settings from an INI file."""
250 # Read file
251 file_settings = configparser.ConfigParser()
252 file_settings.read(file_path)
254 # Set internal settings based on data read from FILE if they were read
255 # successfully
256 if file_settings.sections() != []:
257 section = "general"
258 option_names = ("objectives", )
259 for option in option_names:
260 if file_settings.has_option(section, option):
261 value = [resolve_objective(obj) for obj in
262 file_settings.get(section, option).split(",")]
263 self.set_general_sparkle_objectives(value, state)
264 file_settings.remove_option(section, option)
266 # Comma so python understands it's a tuple...
267 option_names = ("configurator", )
268 for option in option_names:
269 if file_settings.has_option(section, option):
270 value = file_settings.get(section, option)
271 self.set_general_sparkle_configurator(value, state)
272 file_settings.remove_option(section, option)
274 option_names = ("solver_cutoff_time", "target_cutoff_time",
275 "cutoff_time_each_solver_call")
276 for option in option_names:
277 if file_settings.has_option(section, option):
278 value = file_settings.getint(section, option)
279 self.set_general_solver_cutoff_time(value, state)
280 file_settings.remove_option(section, option)
282 option_names = ("extractor_cutoff_time",
283 "cutoff_time_each_feature_computation")
284 for option in option_names:
285 if file_settings.has_option(section, option):
286 value = file_settings.getint(section, option)
287 self.set_general_extractor_cutoff_time(value, state)
288 file_settings.remove_option(section, option)
290 option_names = ("run_on", )
291 for option in option_names:
292 if file_settings.has_option(section, option):
293 value = file_settings.get(section, option)
294 self.set_run_on(value, state)
295 file_settings.remove_option(section, option)
297 option_names = ("verbosity", )
298 for option in option_names:
299 if file_settings.has_option(section, option):
300 value = VerbosityLevel.from_string(
301 file_settings.get(section, option))
302 self.set_general_verbosity(value, state)
303 file_settings.remove_option(section, option)
305 option_names = ("check_interval", )
306 for option in option_names:
307 if file_settings.has_option(section, option):
308 value = int(file_settings.get(section, option))
309 self.set_general_check_interval(value, state)
310 file_settings.remove_option(section, option)
312 section = "configuration"
313 option_names = ("solver_calls", )
314 for option in option_names:
315 if file_settings.has_option(section, option):
316 value = file_settings.getint(section, option)
317 self.set_configurator_solver_calls(value, state)
318 file_settings.remove_option(section, option)
320 option_names = ("number_of_runs", )
321 for option in option_names:
322 if file_settings.has_option(section, option):
323 value = file_settings.getint(section, option)
324 self.set_configurator_number_of_runs(value, state)
325 file_settings.remove_option(section, option)
327 option_name = "max_iterations"
328 if file_settings.has_option(section, option_name):
329 value = file_settings.getint(section, option_name)
330 self.set_configurator_max_iterations(value, state)
331 file_settings.remove_option(section, option_name)
333 section = "smac2"
334 option_names = ("wallclock_time", )
335 for option in option_names:
336 if file_settings.has_option(section, option):
337 value = file_settings.getint(section, option)
338 self.set_smac2_wallclock_time(value, state)
339 file_settings.remove_option(section, option)
341 option_names = ("cpu_time", )
342 for option in option_names:
343 if file_settings.has_option(section, option):
344 value = file_settings.getint(section, option)
345 self.set_smac2_cpu_time(value, state)
346 file_settings.remove_option(section, option)
348 option_names = ("target_cutoff_length", "each_run_cutoff_length")
349 for option in option_names:
350 if file_settings.has_option(section, option):
351 value = file_settings.get(section, option)
352 self.set_smac2_target_cutoff_length(value, state)
353 file_settings.remove_option(section, option)
355 option_names = ("use_cpu_time_in_tunertime", "countSMACTimeAsTunerTime")
356 for option in option_names:
357 if file_settings.has_option(section, option):
358 value = file_settings.getboolean(section, option)
359 self.set_smac2_use_cpu_time_in_tunertime(value, state)
360 file_settings.remove_option(section, option)
362 option_names = ("cli_cores", )
363 for option in option_names:
364 if file_settings.has_option(section, option):
365 value = file_settings.getint(section, option)
366 self.set_smac2_cli_cores(value, state)
367 file_settings.remove_option(section, option)
369 options_names = ("iteration_limit", "numIterations", "numberOfIterations",
370 "max_iterations")
371 for option in options_names:
372 if file_settings.has_option(section, option):
373 value = file_settings.getint(section, option)
374 self.set_smac2_max_iterations(value, state)
375 file_settings.remove_option(section, option)
377 section = "smac3"
379 option_names = ("n_trials", "number_of_trials", "solver_calls")
380 for option in option_names:
381 if file_settings.has_option(section, option):
382 value = file_settings.getint(section, option)
383 self.set_smac3_number_of_trials(value, state)
384 file_settings.remove_option(section, option)
386 options_names = ("facade", "smac_facade", "smac3_facade")
387 for option in options_names:
388 if file_settings.has_option(section, option):
389 value = file_settings.get(section, option)
390 self.set_smac3_smac_facade(value, state)
391 file_settings.remove_option(section, option)
393 option_names = ("max_ratio", "facade_max_ratio", "initial_trials_max_ratio")
394 for option in option_names:
395 if file_settings.has_option(section, option):
396 value = file_settings.getfloat(section, option)
397 self.set_smac3_facade_max_ratio(value, state)
398 file_settings.remove_option(section, option)
400 options_names = ("crash_cost", )
401 for option in options_names:
402 if file_settings.has_option(section, option):
403 value = file_settings.get(section, option)
404 self.set_smac3_crash_cost(value, state)
405 file_settings.remove_option(section, option)
407 options_names = ("termination_cost_threshold", )
408 for option in options_names:
409 if file_settings.has_option(section, option):
410 value = file_settings.get(section, option)
411 self.set_smac3_termination_cost_threshold(value, state)
412 file_settings.remove_option(section, option)
414 options_names = ("walltime_limit", "wallclock_time")
415 for option in options_names:
416 if file_settings.has_option(section, option):
417 value = file_settings.getfloat(section, option)
418 self.set_smac3_walltime_limit(value, state)
419 file_settings.remove_option(section, option)
421 options_names = ("cputime_limit", )
422 for option in options_names:
423 if file_settings.has_option(section, option):
424 value = file_settings.getfloat(section, option)
425 self.set_smac3_cputime_limit(value, state)
426 file_settings.remove_option(section, option)
428 options_names = ("use_default_config", )
429 for option in options_names:
430 if file_settings.has_option(section, option):
431 value = file_settings.getboolean(section, option)
432 self.set_smac3_use_default_config(value, state)
433 file_settings.remove_option(section, option)
435 options_names = ("min_budget", )
436 for option in options_names:
437 if file_settings.has_option(section, option):
438 value = file_settings.getfloat(section, option)
439 self.set_smac3_min_budget(value, state)
440 file_settings.remove_option(section, option)
442 options_names = ("max_budget", )
443 for option in options_names:
444 if file_settings.has_option(section, option):
445 value = file_settings.getfloat(section, option)
446 self.set_smac3_max_budget(value, state)
447 file_settings.remove_option(section, option)
449 section = "irace"
450 option_names = ("max_time", )
451 for option in option_names:
452 if file_settings.has_option(section, option):
453 value = file_settings.getint(section, option)
454 self.set_irace_max_time(value, state)
455 file_settings.remove_option(section, option)
457 option_names = ("max_experiments", )
458 for option in option_names:
459 if file_settings.has_option(section, option):
460 value = file_settings.getint(section, option)
461 self.set_irace_max_experiments(value, state)
462 file_settings.remove_option(section, option)
464 option_names = ("first_test", )
465 for option in option_names:
466 if file_settings.has_option(section, option):
467 value = file_settings.getint(section, option)
468 self.set_irace_first_test(value, state)
469 file_settings.remove_option(section, option)
471 option_names = ("mu", )
472 for option in option_names:
473 if file_settings.has_option(section, option):
474 value = file_settings.getint(section, option)
475 self.set_irace_mu(value, state)
476 file_settings.remove_option(section, option)
478 option_names = ("nb_iterations", "iterations", "max_iterations")
479 for option in option_names:
480 if file_settings.has_option(section, option):
481 value = file_settings.getint(section, option)
482 self.set_irace_max_iterations(value, state)
483 file_settings.remove_option(section, option)
485 section = "paramils"
487 option_names = ("min_runs", )
488 for option in option_names:
489 if file_settings.has_option(section, option):
490 value = file_settings.getint(section, option)
491 self.set_paramils_min_runs(value, state)
492 file_settings.remove_option(section, option)
494 option_names = ("max_runs", )
495 for option in option_names:
496 if file_settings.has_option(section, option):
497 value = file_settings.getint(section, option)
498 self.set_paramils_max_runs(value, state)
499 file_settings.remove_option(section, option)
501 option_names = ("cputime_limit", "cputime_limit", "tunertime_limit",
502 "tuner_timeout", "tunerTimeout")
503 for option in option_names:
504 if file_settings.has_option(section, option):
505 value = file_settings.getint(section, option)
506 self.set_paramils_tuner_timeout(value, state)
507 file_settings.remove_option(section, option)
509 option_names = ("random_restart", )
510 for option in option_names:
511 if file_settings.has_option(section, option):
512 value = file_settings.getfloat(section, option)
513 self.set_paramils_random_restart(value, state)
514 file_settings.remove_option(section, option)
516 option_names = ("focused_approach", )
517 for option in option_names:
518 if file_settings.has_option(section, option):
519 value = file_settings.getboolean(section, option)
520 self.set_paramils_focused_approach(value, state)
521 file_settings.remove_option(section, option)
523 option_names = ("use_cpu_time_in_tunertime", )
524 for option in option_names:
525 if file_settings.has_option(section, option):
526 value = file_settings.getboolean(section, option)
527 self.set_paramils_use_cpu_time_in_tunertime(value, state)
528 file_settings.remove_option(section, option)
530 option_names = ("cli_cores", )
531 for option in option_names:
532 if file_settings.has_option(section, option):
533 value = file_settings.getint(section, option)
534 self.set_paramils_cli_cores(value, state)
535 file_settings.remove_option(section, option)
537 options_names = ("iteration_limit", "numIterations", "numberOfIterations",
538 "max_iterations")
539 for option in options_names:
540 if file_settings.has_option(section, option):
541 value = file_settings.getint(section, option)
542 self.set_paramils_max_iterations(value, state)
543 file_settings.remove_option(section, option)
545 section = "selection"
547 option_names = ("selector_class", )
548 for option in option_names:
549 if file_settings.has_option(section, option):
550 value = file_settings.get(section, option)
551 self.set_selection_class(value, state)
552 file_settings.remove_option(section, option)
554 option_names = ("selector_model")
555 for option in option_names:
556 if file_settings.has_option(section, option):
557 value = file_settings.get(section, option)
558 self.set_selection_model(value, state)
559 file_settings.remove_option(section, option)
561 section = "slurm"
562 option_names = ("number_of_jobs_in_parallel", "num_job_in_parallel")
563 for option in option_names:
564 if file_settings.has_option(section, option):
565 value = file_settings.getint(section, option)
566 self.set_number_of_jobs_in_parallel(value, state)
567 file_settings.remove_option(section, option)
569 option_names = ("max_parallel_runs_per_node", "clis_per_node")
570 for option in option_names:
571 if file_settings.has_option(section, option):
572 value = file_settings.getint(section, option)
573 self.set_slurm_max_parallel_runs_per_node(value, state)
574 file_settings.remove_option(section, option)
576 option_names = ("job_submission_limit", "max_jobs_submit")
577 for option in option_names:
578 if file_settings.has_option(section, option):
579 value = file_settings.getint(section, option)
580 self.set_slurm_job_submission_limit(value, state)
581 file_settings.remove_option(section, option)
583 option_names = ("job_prepend", "prepend", "prepend_script")
584 for option in option_names:
585 if file_settings.has_option(section, option):
586 value = file_settings.get(section, option)
587 self.set_slurm_job_prepend(value, state)
588 file_settings.remove_option(section, option)
590 section = "ablation"
591 option_names = ("racing", "ablation_racing")
592 for option in option_names:
593 if file_settings.has_option(section, option):
594 value = file_settings.getboolean(section, option)
595 self.set_ablation_racing_flag(value, state)
596 file_settings.remove_option(section, option)
598 section = "parallel_portfolio"
599 option_names = ("check_interval", )
600 for option in option_names:
601 if file_settings.has_option(section, option):
602 value = int(file_settings.get(section, option))
603 self.set_parallel_portfolio_check_interval(value, state)
604 file_settings.remove_option(section, option)
606 option_names = ("num_seeds_per_solver", )
607 for option in option_names:
608 if file_settings.has_option(section, option):
609 value = int(file_settings.get(section, option))
610 self.set_parallel_portfolio_number_of_seeds_per_solver(value, state)
611 file_settings.remove_option(section, option)
613 # TODO: Report on any unknown settings that were read
614 sections = file_settings.sections()
616 for section in sections:
617 for option in file_settings[section]:
618 # TODO: Should check the options are valid Slurm options
619 if section == "slurm":
620 value = file_settings.get(section, option)
621 self.add_slurm_extra_option(option, value, state)
622 else:
623 print(f'Unrecognised section - option combination: "{section} '
624 f'{option}" in file {file_path} ignored')
626 # Print error if unable to read the settings
627 elif Path(file_path).exists():
628 print(f"ERROR: Failed to read settings from {file_path} The file may have "
629 "been empty or be in another format than INI. Default Setting values "
630 "will be used.")
632 def write_used_settings(self: Settings) -> None:
633 """Write the used settings to the default locations."""
634 # Write to latest settings file
635 self.write_settings_ini(self.DEFAULT_settings_dir / "latest.ini")
637 def write_settings_ini(self: Settings, file_path: Path) -> None:
638 """Write the settings to an INI file."""
639 # Create needed directories if they don't exist
640 file_path.parent.mkdir(parents=True, exist_ok=True)
641 slurm_extra_section_options = None
642 if self.__settings.has_section("slurm_extra"):
643 # Slurm extra options are not written as a seperate section
644 slurm_extra_section_options = {}
645 for key in self.__settings["slurm_extra"]:
646 self.__settings["slurm"][key] = self.__settings["slurm_extra"][key]
647 slurm_extra_section_options[key] = self.__settings["slurm_extra"][key]
648 self.__settings.remove_section("slurm_extra")
649 # We do not write None values
650 removed = []
651 for section in self.__settings.sections():
652 for option in self.__settings[section]:
653 try:
654 if ast.literal_eval(str(self.__settings[section][option])) is None:
655 del self.__settings[section][option]
656 removed.append((section, option))
657 except Exception:
658 pass
659 # Write the settings to file
660 with file_path.open("w") as settings_file:
661 self.__settings.write(settings_file)
662 # Rebuild slurm extra if needed
663 if slurm_extra_section_options is not None:
664 self.__settings.add_section("slurm_extra")
665 for key in slurm_extra_section_options:
666 self.__settings["slurm_extra"][key] = slurm_extra_section_options[key]
667 # Rebuild None if needed
668 for section, option in removed:
669 self.__settings[section][option] = "None"
671 def __init_section(self: Settings, section: str) -> None:
672 if section not in self.__settings:
673 self.__settings[section] = {}
675 @staticmethod
676 def __check_setting_state(current_state: SettingState,
677 new_state: SettingState, name: str) -> bool:
678 change_setting_ok = True
680 if current_state == SettingState.FILE and new_state == SettingState.DEFAULT:
681 change_setting_ok = False
682 print(f"Warning: Attempting to overwrite setting for {name} with default "
683 "value; keeping the value read from file!")
684 elif (current_state == SettingState.CMD_LINE
685 and new_state == SettingState.DEFAULT):
686 change_setting_ok = False
687 print(f"Warning: Attempting to overwrite setting for {name} with default "
688 "value; keeping the value read from command line!")
689 elif current_state == SettingState.CMD_LINE and new_state == SettingState.FILE:
690 change_setting_ok = False
691 print(f"Warning: Attempting to overwrite setting for {name} with value from "
692 "file; keeping the value read from command line!")
694 return change_setting_ok
696 # General settings ###
697 def set_general_sparkle_objectives(
698 self: Settings,
699 value: list[SparkleObjective] = [DEFAULT_general_sparkle_objective, ],
700 origin: SettingState = SettingState.DEFAULT) -> None:
701 """Set the sparkle objective."""
702 section = "general"
703 name = "objectives"
705 if value is not None and self.__check_setting_state(
706 self.__general_sparkle_objective_set, origin, name):
707 if isinstance(value, list):
708 value = ",".join([str(obj) for obj in value])
709 else:
710 value = str(value)
712 # Append standard Sparkle Objectives
713 if "status" not in value:
714 value += ",status:metric"
715 if "cpu_time" not in value:
716 value += ",cpu_time:metric"
717 if "wall_time" not in value:
718 value += ",wall_time:metric"
719 if "memory" not in value:
720 value += ",memory:metric"
722 self.__init_section(section)
723 self.__general_sparkle_objective_set = origin
724 self.__settings[section][name] = value
726 def get_general_sparkle_objectives(
727 self: Settings,
728 filter_metric: bool = False) -> list[SparkleObjective]:
729 """Return the Sparkle objectives."""
730 if self.__general_sparkle_objective_set == SettingState.NOT_SET:
731 self.set_general_sparkle_objectives()
733 objectives = [resolve_objective(obj)
734 for obj in self.__settings["general"]["objectives"].split(",")]
736 if filter_metric:
737 return [obj for obj in objectives if not obj.metric]
739 return objectives
741 def set_general_sparkle_configurator(
742 self: Settings,
743 value: str = DEFAULT_general_sparkle_configurator,
744 origin: SettingState = SettingState.DEFAULT) -> None:
745 """Set the Sparkle configurator."""
746 section = "general"
747 name = "configurator"
748 if value is not None and self.__check_setting_state(
749 self.__general_sparkle_configurator_set, origin, name):
750 self.__init_section(section)
751 self.__general_sparkle_configurator_set = origin
752 self.__settings[section][name] = value
754 def get_general_sparkle_configurator(self: Settings) -> Configurator:
755 """Return the configurator init method."""
756 if self.__general_sparkle_configurator_set == SettingState.NOT_SET:
757 self.set_general_sparkle_configurator()
758 configurator_var = self.__settings["general"]["configurator"]
759 if (self.__general_sparkle_configurator is None
760 or self.__general_sparkle_configurator.name != configurator_var):
761 configurator_subclass =\
762 cim.resolve_configurator(self.__settings["general"]["configurator"])
763 if configurator_subclass is not None:
764 self.__general_sparkle_configurator = configurator_subclass()
765 else:
766 print("WARNING: Configurator class name not recognised: "
767 f'{self.__settings["general"]["configurator"]}. '
768 "Configurator not set.")
769 return self.__general_sparkle_configurator
771 def get_configurator_output_path(self: Settings, configurator: Configurator) -> Path:
772 """Return the configurator output path."""
773 return self.DEFAULT_configuration_output / configurator.name
775 def set_general_solver_cutoff_time(
776 self: Settings, value: int = DEFAULT_general_solver_cutoff_time,
777 origin: SettingState = SettingState.DEFAULT) -> None:
778 """Set the cutoff time in seconds for Solver."""
779 section = "general"
780 name = "solver_cutoff_time"
781 if value is not None and self.__check_setting_state(
782 self.__general_solver_cutoff_time_set, origin, name):
783 self.__init_section(section)
784 self.__general_solver_cutoff_time_set = origin
785 self.__settings[section][name] = str(value)
787 def get_general_solver_cutoff_time(self: Settings) -> int:
788 """Return the cutoff time in seconds for Solvers."""
789 if self.__general_solver_cutoff_time_set == SettingState.NOT_SET:
790 self.set_general_solver_cutoff_time()
791 return int(self.__settings["general"]["solver_cutoff_time"])
793 def set_general_extractor_cutoff_time(
794 self: Settings, value: int = DEFAULT_general_extractor_cutoff_time,
795 origin: SettingState = SettingState.DEFAULT) -> None:
796 """Set the cutoff time in seconds for feature extraction."""
797 section = "general"
798 name = "extractor_cutoff_time"
800 if value is not None and self.__check_setting_state(
801 self.__general_extractor_cutoff_time_set, origin, name):
802 self.__init_section(section)
803 self.__general_extractor_cutoff_time_set = origin
804 self.__settings[section][name] = str(value)
806 def get_general_extractor_cutoff_time(self: Settings) -> int:
807 """Return the cutoff time in seconds for feature extraction."""
808 if self.__general_extractor_cutoff_time_set == SettingState.NOT_SET:
809 self.set_general_extractor_cutoff_time()
810 return int(self.__settings["general"]["extractor_cutoff_time"])
812 def set_number_of_jobs_in_parallel(
813 self: Settings, value: int = DEFAULT_number_of_jobs_in_parallel,
814 origin: SettingState = SettingState.DEFAULT) -> None:
815 """Set the number of runs Sparkle can do in parallel."""
816 section = "slurm"
817 name = "number_of_jobs_in_parallel"
819 if value is not None and self.__check_setting_state(
820 self.__number_of_jobs_in_parallel_set, origin, name):
821 self.__init_section(section)
822 self.__number_of_jobs_in_parallel_set = origin
823 self.__settings[section][name] = str(value)
825 def get_number_of_jobs_in_parallel(self: Settings) -> int:
826 """Return the number of runs Sparkle can do in parallel."""
827 if self.__number_of_jobs_in_parallel_set == SettingState.NOT_SET:
828 self.set_number_of_jobs_in_parallel()
830 return int(self.__settings["slurm"]["number_of_jobs_in_parallel"])
832 def set_general_verbosity(
833 self: Settings, value: VerbosityLevel = DEFAULT_general_verbosity,
834 origin: SettingState = SettingState.DEFAULT) -> None:
835 """Set the general verbosity to use."""
836 section = "general"
837 name = "verbosity"
839 if value is not None and self.__check_setting_state(
840 self.__general_verbosity_set, origin, name):
841 self.__init_section(section)
842 self.__general_verbosity_set = origin
843 self.__settings[section][name] = value.name
845 def get_general_verbosity(self: Settings) -> VerbosityLevel:
846 """Return the general verbosity."""
847 if self.__general_verbosity_set == SettingState.NOT_SET:
848 self.set_general_verbosity()
850 return VerbosityLevel.from_string(
851 self.__settings["general"]["verbosity"])
853 def set_general_check_interval(
854 self: Settings,
855 value: int = DEFAULT_general_check_interval,
856 origin: SettingState = SettingState.DEFAULT) -> None:
857 """Set the general check interval."""
858 section = "general"
859 name = "check_interval"
861 if value is not None and self.__check_setting_state(
862 self.__general_check_interval_set, origin, name):
863 self.__init_section(section)
864 self.__general_check_interval_set = origin
865 self.__settings[section][name] = str(value)
867 def get_general_check_interval(self: Settings) -> int:
868 """Return the general check interval."""
869 if self.__general_check_interval_set == SettingState.NOT_SET:
870 self.set_general_check_interval()
872 return int(self.__settings["general"]["check_interval"])
874 # Configuration settings General ###
876 def get_configurator_settings(self: Settings,
877 configurator_name: str) -> dict[str, any]:
878 """Return the configurator settings."""
879 configurator_settings = {
880 "solver_calls": self.get_configurator_solver_calls(),
881 "solver_cutoff_time": self.get_general_solver_cutoff_time(),
882 "max_iterations": self.get_configurator_max_iterations()
883 }
884 # In the settings below, we default to the configurator general settings if no
885 # specific configurator settings are given, by using the [None] or [Value]
886 if configurator_name == cim.SMAC2.__name__:
887 # Return all settings from the SMAC2 section
888 configurator_settings.update({
889 "cpu_time": self.get_smac2_cpu_time(),
890 "wallclock_time": self.get_smac2_wallclock_time(),
891 "target_cutoff_length": self.get_smac2_target_cutoff_length(),
892 "use_cpu_time_in_tunertime": self.get_smac2_use_cpu_time_in_tunertime(),
893 "cli_cores": self.get_smac2_cli_cores(),
894 "max_iterations": self.get_smac2_max_iterations()
895 or configurator_settings["max_iterations"],
896 })
897 elif configurator_name == cim.SMAC3.__name__:
898 # Return all settings from the SMAC3 section
899 del configurator_settings["max_iterations"] # SMAC3 does not have this?
900 configurator_settings.update({
901 "smac_facade": self.get_smac3_smac_facade(),
902 "max_ratio": self.get_smac3_facade_max_ratio(),
903 "crash_cost": self.get_smac3_crash_cost(),
904 "termination_cost_threshold":
905 self.get_smac3_termination_cost_threshold(),
906 "walltime_limit": self.get_smac3_walltime_limit(),
907 "cputime_limit": self.get_smac3_cputime_limit(),
908 "use_default_config": self.get_smac3_use_default_config(),
909 "min_budget": self.get_smac3_min_budget(),
910 "max_budget": self.get_smac3_max_budget(),
911 "solver_calls": self.get_smac3_number_of_trials()
912 or configurator_settings["solver_calls"],
913 })
914 # Do not pass None values to SMAC3, it Scenario resolves default settings
915 configurator_settings = {key: value
916 for key, value in configurator_settings.items()
917 if value is not None}
918 elif configurator_name == cim.IRACE.__name__:
919 # Return all settings from the IRACE section
920 configurator_settings.update({
921 "solver_calls": self.get_irace_max_experiments(),
922 "max_time": self.get_irace_max_time(),
923 "first_test": self.get_irace_first_test(),
924 "mu": self.get_irace_mu(),
925 "max_iterations": self.get_irace_max_iterations()
926 or configurator_settings["max_iterations"],
927 })
928 if (configurator_settings["solver_calls"] == 0
929 and configurator_settings["max_time"] == 0): # Default to base
930 configurator_settings["solver_calls"] =\
931 self.get_configurator_solver_calls()
932 elif configurator_name == cim.ParamILS.__name__:
933 configurator_settings.update({
934 "tuner_timeout": self.get_paramils_tuner_timeout(),
935 "min_runs": self.get_paramils_min_runs(),
936 "max_runs": self.get_paramils_max_runs(),
937 "focused_ils": self.get_paramils_focused_approach(),
938 "initial_configurations": self.get_paramils_initial_configurations(),
939 "random_restart": self.get_paramils_random_restart(),
940 "cli_cores": self.get_paramils_cli_cores(),
941 "use_cpu_time_in_tunertime":
942 self.get_paramils_use_cpu_time_in_tunertime(),
943 "max_iterations": self.set_paramils_max_iterations()
944 or configurator_settings["max_iterations"],
945 })
946 return configurator_settings
948 def set_configurator_solver_calls(
949 self: Settings, value: int = DEFAULT_configurator_solver_calls,
950 origin: SettingState = SettingState.DEFAULT) -> None:
951 """Set the number of solver calls."""
952 section = "configuration"
953 name = "solver_calls"
955 if value is not None and self.__check_setting_state(
956 self.__config_solver_calls_set, origin, name):
957 self.__init_section(section)
958 self.__config_solver_calls_set = origin
959 self.__settings[section][name] = str(value)
961 def get_configurator_solver_calls(self: Settings) -> int | None:
962 """Return the maximum number of solver calls the configurator can do."""
963 if self.__config_solver_calls_set == SettingState.NOT_SET:
964 self.set_configurator_solver_calls()
966 return int(self.__settings["configuration"]["solver_calls"])
968 def set_configurator_number_of_runs(
969 self: Settings, value: int = DEFAULT_configurator_number_of_runs,
970 origin: SettingState = SettingState.DEFAULT) -> None:
971 """Set the number of configuration runs."""
972 section = "configuration"
973 name = "number_of_runs"
975 if value is not None and self.__check_setting_state(
976 self.__config_number_of_runs_set, origin, name):
977 self.__init_section(section)
978 self.__config_number_of_runs_set = origin
979 self.__settings[section][name] = str(value)
981 def get_configurator_number_of_runs(self: Settings) -> int:
982 """Return the number of configuration runs."""
983 if self.__config_number_of_runs_set == SettingState.NOT_SET:
984 self.set_configurator_number_of_runs()
986 return int(self.__settings["configuration"]["number_of_runs"])
988 def set_configurator_max_iterations(
989 self: Settings, value: int = DEFAULT_configurator_maximum_iterations,
990 origin: SettingState = SettingState.DEFAULT) -> None:
991 """Set the number of configuration runs."""
992 section = "configuration"
993 name = "max_iterations"
995 if self.__check_setting_state(
996 self.__config_max_iterations_set, origin, name):
997 self.__init_section(section)
998 self.__config_max_iterations_set = origin
999 self.__settings[section][name] = str(value)
1001 def get_configurator_max_iterations(self: Settings) -> int | None:
1002 """Get the maximum number of configurator iterations."""
1003 if self.__config_max_iterations_set == SettingState.NOT_SET:
1004 self.set_configurator_max_iterations()
1005 max_iterations = self.__settings["configuration"]["max_iterations"]
1006 return int(max_iterations) if max_iterations.isdigit() else None
1008 # Configuration: SMAC2 specific settings ###
1010 def set_smac2_wallclock_time(
1011 self: Settings, value: int = DEFAULT_smac2_wallclock_time,
1012 origin: SettingState = SettingState.DEFAULT) -> None:
1013 """Set the budget per configuration run in seconds (wallclock)."""
1014 section = "smac2"
1015 name = "wallclock_time"
1017 if self.__check_setting_state(
1018 self.__smac2_wallclock_time_set, origin, name):
1019 self.__init_section(section)
1020 self.__smac2_wallclock_time_set = origin
1021 self.__settings[section][name] = str(value)
1023 def get_smac2_wallclock_time(self: Settings) -> int | None:
1024 """Return the budget per configuration run in seconds (wallclock)."""
1025 if self.__smac2_wallclock_time_set == SettingState.NOT_SET:
1026 self.set_smac2_wallclock_time()
1027 wallclock_time = self.__settings["smac2"]["wallclock_time"]
1028 return int(wallclock_time) if wallclock_time.isdigit() else None
1030 def set_smac2_cpu_time(
1031 self: Settings, value: int = DEFAULT_smac2_cpu_time,
1032 origin: SettingState = SettingState.DEFAULT) -> None:
1033 """Set the budget per configuration run in seconds (cpu)."""
1034 section = "smac2"
1035 name = "cpu_time"
1037 if self.__check_setting_state(
1038 self.__smac2_cpu_time_set, origin, name):
1039 self.__init_section(section)
1040 self.__smac2_cpu_time_set = origin
1041 self.__settings[section][name] = str(value)
1043 def get_smac2_cpu_time(self: Settings) -> int | None:
1044 """Return the budget per configuration run in seconds (cpu)."""
1045 if self.__smac2_cpu_time_set == SettingState.NOT_SET:
1046 self.set_smac2_cpu_time()
1047 cpu_time = self.__settings["smac2"]["cpu_time"]
1048 return int(cpu_time) if cpu_time.isdigit() else None
1050 def set_smac2_target_cutoff_length(
1051 self: Settings, value: str = DEFAULT_smac2_target_cutoff_length,
1052 origin: SettingState = SettingState.DEFAULT) -> None:
1053 """Set the target algorithm cutoff length."""
1054 section = "smac2"
1055 name = "target_cutoff_length"
1057 if value is not None and self.__check_setting_state(
1058 self.__smac2_target_cutoff_length_set, origin, name):
1059 self.__init_section(section)
1060 self.__smac2_target_cutoff_length_set = origin
1061 self.__settings[section][name] = str(value)
1063 def get_smac2_target_cutoff_length(self: Settings) -> str:
1064 """Return the target algorithm cutoff length.
1066 'A domain specific measure of when the algorithm should consider itself done.'
1068 Returns:
1069 The target algorithm cutoff length.
1070 """
1071 if self.__smac2_target_cutoff_length_set == SettingState.NOT_SET:
1072 self.set_smac2_target_cutoff_length()
1073 return self.__settings["smac2"]["target_cutoff_length"]
1075 def set_smac2_use_cpu_time_in_tunertime(
1076 self: Settings, value: bool = DEFAULT_smac2_use_cpu_time_in_tunertime,
1077 origin: SettingState = SettingState.DEFAULT) -> None:
1078 """Set whether to use CPU time in tunertime."""
1079 section = "smac2"
1080 name = "use_cpu_time_in_tunertime"
1082 if self.__check_setting_state(
1083 self.__smac2_use_cpu_time_in_tunertime_set, origin, name):
1084 self.__init_section(section)
1085 self.__smac2_use_cpu_time_in_tunertime_set = origin
1086 self.__settings[section][name] = str(value)
1088 def get_smac2_use_cpu_time_in_tunertime(self: Settings) -> bool:
1089 """Return whether to use CPU time in tunertime."""
1090 if self.__smac2_use_cpu_time_in_tunertime_set == SettingState.NOT_SET:
1091 self.set_smac2_use_cpu_time_in_tunertime()
1092 return ast.literal_eval(self.__settings["smac2"]["use_cpu_time_in_tunertime"])
1094 def set_smac2_cli_cores(
1095 self: Settings, value: int = DEFAULT_smac2_cli_cores,
1096 origin: SettingState = SettingState.DEFAULT) -> None:
1097 """Set the number of cores to use for SMAC2 CLI."""
1098 section = "smac2"
1099 name = "cli_cores"
1101 if self.__check_setting_state(
1102 self.__smac2_cli_cores_set, origin, name):
1103 self.__init_section(section)
1104 self.__smac2_cli_cores_set = origin
1105 self.__settings[section][name] = str(value)
1107 def get_smac2_cli_cores(self: Settings) -> int | None:
1108 """Number of cores to use to execute runs.
1110 In other words, the number of requests to run at a given time.
1111 """
1112 if self.__smac2_cli_cores_set == SettingState.NOT_SET:
1113 self.set_smac2_cli_cores()
1114 cli_cores = self.__settings["smac2"]["cli_cores"]
1115 return int(cli_cores) if cli_cores.isdigit() else None
1117 def set_smac2_max_iterations(
1118 self: Settings, value: int = DEFAULT_smac2_max_iterations,
1119 origin: SettingState = SettingState.DEFAULT) -> None:
1120 """Set the maximum number of SMAC2 iterations."""
1121 section = "smac2"
1122 name = "max_iterations"
1124 if self.__check_setting_state(
1125 self.__smac2_max_iterations_set, origin, name):
1126 self.__init_section(section)
1127 self.__smac2_max_iterations_set = origin
1128 self.__settings[section][name] = str(value)
1130 def get_smac2_max_iterations(self: Settings) -> int | None:
1131 """Get the maximum number of SMAC2 iterations."""
1132 if self.__smac2_max_iterations_set == SettingState.NOT_SET:
1133 self.set_smac2_max_iterations()
1134 max_iterations = self.__settings["smac2"]["max_iterations"]
1135 return int(max_iterations) if max_iterations.isdigit() else None
1137 # Configuration: SMAC3 specific settings ###
1139 def set_smac3_number_of_trials(
1140 self: Settings, value: int = DEFAULT_smac3_number_of_runs,
1141 origin: SettingState = SettingState.DEFAULT) -> None:
1142 """Set the number of SMAC3 trials."""
1143 section = "smac3"
1144 name = "number_of_runs"
1146 if self.__check_setting_state(
1147 self.__smac3_number_of_trials_set, origin, name):
1148 self.__init_section(section)
1149 self.__smac3_number_of_trials_set = origin
1150 self.__settings[section][name] = str(value)
1152 def get_smac3_number_of_trials(self: Settings) -> int | None:
1153 """Return the number of SMAC3 trials (Solver calls).
1155 'The maximum number of trials (combination of configuration, seed, budget,
1156 and instance, depending on the task) to run.'
1157 """
1158 if self.__smac3_number_of_trials_set == SettingState.NOT_SET:
1159 self.set_smac3_number_of_trials()
1160 number_of_runs = self.__settings["smac3"]["number_of_runs"]
1161 return int(number_of_runs) if number_of_runs.isdigit() else None
1163 def set_smac3_smac_facade(
1164 self: Settings, value: str = DEFAULT_smac3_facade,
1165 origin: SettingState = SettingState.DEFAULT) -> None:
1166 """Set the SMAC3 facade."""
1167 section = "smac3"
1168 name = "facade"
1170 if self.__check_setting_state(self.__smac3_smac_facade_set, origin, name):
1171 self.__init_section(section)
1172 self.__smac3_smac_facade_set = origin
1173 self.__settings[section][name] = str(value)
1175 def get_smac3_smac_facade(self: Settings) -> str:
1176 """Return the SMAC3 facade."""
1177 if self.__smac3_smac_facade_set == SettingState.NOT_SET:
1178 self.set_smac3_smac_facade()
1179 return self.__settings["smac3"]["facade"]
1181 def set_smac3_facade_max_ratio(
1182 self: Settings, value: float = DEFAULT_smac3_facade_max_ratio,
1183 origin: SettingState = SettingState.DEFAULT) -> None:
1184 """Set the SMAC3 facade max ratio."""
1185 section = "smac3"
1186 name = "facade_max_ratio"
1188 if self.__check_setting_state(
1189 self.__smac3_facade_max_ratio_set, origin, name):
1190 self.__init_section(section)
1191 self.__smac3_facade_max_ratio_set = origin
1192 self.__settings[section][name] = str(value)
1194 def get_smac3_facade_max_ratio(self: Settings) -> float:
1195 """Return the SMAC3 facade max ratio."""
1196 if self.__smac3_facade_max_ratio_set == SettingState.NOT_SET:
1197 self.set_smac3_facade_max_ratio()
1198 return ast.literal_eval(self.__settings["smac3"]["facade_max_ratio"])
1200 def set_smac3_crash_cost(self: Settings, value: float = DEFAULT_smac3_crash_cost,
1201 origin: SettingState = SettingState.DEFAULT) -> None:
1202 """Set the SMAC3 objective crash cost."""
1203 section = "smac3"
1204 name = "crash_cost"
1206 if self.__check_setting_state(self.__smac3_crash_cost_set, origin, name):
1207 self.__init_section(section)
1208 self.__smac3_smac_facade_set = origin
1209 self.__settings[section][name] = str(value)
1211 def get_smac3_crash_cost(self: Settings) -> float | list[float]:
1212 """Get the SMAC3 objective crash cost.
1214 'crash_cost : float | list[float], defaults to np.inf
1215 Defines the cost for a failed trial. In case of multi-objective,
1216 each objective can be associated with a different cost.'
1217 """
1218 if self.__smac3_crash_cost_set == SettingState.NOT_SET:
1219 self.set_smac3_crash_cost()
1220 return ast.literal_eval(self.__settings["smac3"]["crash_cost"])
1222 def set_smac3_termination_cost_threshold(
1223 self: Settings,
1224 value: float = DEFAULT_smac3_termination_cost_threshold,
1225 origin: SettingState = SettingState.DEFAULT) -> None:
1226 """Set the SMAC3 termination cost threshold."""
1227 section = "smac3"
1228 name = "termination_cost_threshold"
1230 if self.__check_setting_state(
1231 self.__smac3_termination_cost_threshold_set, origin, name):
1232 self.__init_section(section)
1233 self.__smac3_termination_cost_threshold_set = origin
1234 self.__settings[section][name] = str(value)
1236 def get_smac3_termination_cost_threshold(self: Settings) -> float | list[float]:
1237 """Get the SMAC3 termination cost threshold.
1239 'Defines a cost threshold when the optimization should stop. In case of
1240 multi-objective, each objective *must* be associated with a cost.
1241 The optimization stops when all objectives crossed the threshold.'
1242 """
1243 if self.__smac3_termination_cost_threshold_set == SettingState.NOT_SET:
1244 self.set_smac3_termination_cost_threshold()
1245 return ast.literal_eval(self.__settings["smac3"]["termination_cost_threshold"])
1247 def set_smac3_walltime_limit(
1248 self: Settings, value: float = DEFAULT_smac3_walltime_limit,
1249 origin: SettingState = SettingState.DEFAULT) -> None:
1250 """Set the SMAC3 walltime limit."""
1251 section = "smac3"
1252 name = "walltime_limit"
1254 if self.__check_setting_state(self.__smac3_walltime_limit_set, origin, name):
1255 self.__init_section(section)
1256 self.__smac3_walltime_limit_set = origin
1257 self.__settings[section][name] = str(value)
1259 def get_smac3_walltime_limit(self: Settings) -> float:
1260 """Get the SMAC3 walltime limit.
1262 'The maximum time in seconds that SMAC is allowed to run.'
1263 """
1264 if self.__smac3_walltime_limit_set == SettingState.NOT_SET:
1265 self.set_smac3_walltime_limit()
1266 return ast.literal_eval(self.__settings["smac3"]["walltime_limit"])
1268 def set_smac3_cputime_limit(
1269 self: Settings, value: float = DEFAULT_smac3_cputime_limit,
1270 origin: SettingState = SettingState.DEFAULT) -> None:
1271 """Set the SMAC3 CPU time limit."""
1272 section = "smac3"
1273 name = "cputime_limit"
1275 if self.__check_setting_state(self.__smac3_cputime_limit_set, origin, name):
1276 self.__init_section(section)
1277 self.__smac3_cputime_limit_set = origin
1278 self.__settings[section][name] = str(value)
1280 def get_smac3_cputime_limit(self: Settings) -> float:
1281 """Get the SMAC3 CPU time limit.
1283 'The maximum CPU time in seconds that SMAC is allowed to run.'
1284 """
1285 if self.__smac3_cputime_limit_set == SettingState.NOT_SET:
1286 self.set_smac3_cputime_limit()
1287 return ast.literal_eval(self.__settings["smac3"]["cputime_limit"])
1289 def set_smac3_use_default_config(
1290 self: Settings, value: bool = DEFAULT_smac3_use_default_config,
1291 origin: SettingState = SettingState.DEFAULT) -> None:
1292 """Set the SMAC3 to use default config."""
1293 section = "smac3"
1294 name = "use_default_config"
1296 if self.__check_setting_state(self.__smac3_use_default_config_set, origin, name):
1297 self.__init_section(section)
1298 self.__smac3_use_default_config_set = origin
1299 self.__settings[section][name] = str(value)
1301 def get_smac3_use_default_config(self: Settings) -> bool:
1302 """Get the SMAC3 to use default config.
1304 'If True, the configspace's default configuration is evaluated in the
1305 initial design. For historic benchmark reasons, this is False by default.
1306 Notice, that this will result in n_configs + 1 for the initial design.
1307 Respecting n_trials, this will result in one fewer evaluated
1308 configuration in the optimization.'
1309 """
1310 if self.__smac3_use_default_config_set == SettingState.NOT_SET:
1311 self.set_smac3_use_default_config()
1312 return ast.literal_eval(self.__settings["smac3"]["use_default_config"])
1314 def set_smac3_min_budget(
1315 self: Settings, value: int | float = DEFAULT_smac3_min_budget,
1316 origin: SettingState = SettingState.DEFAULT) -> None:
1317 """Set the SMAC3 min budget."""
1318 section = "smac3"
1319 name = "min_budget"
1321 if self.__check_setting_state(self.__smac3_min_budget_set, origin, name):
1322 self.__init_section(section)
1323 self.__smac3_min_budget_set = origin
1324 self.__settings[section][name] = str(value)
1326 def get_smac3_min_budget(self: Settings) -> int | float:
1327 """Get the SMAC3 min budget.
1329 'The minimum budget (epochs, subset size, number of instances, ...) that
1330 is used for the optimization. Use this argument if you use multi-fidelity
1331 or instance optimization.'
1332 """
1333 if self.__smac3_min_budget_set == SettingState.NOT_SET:
1334 self.set_smac3_min_budget()
1335 return ast.literal_eval(self.__settings["smac3"]["min_budget"])
1337 def set_smac3_max_budget(
1338 self: Settings, value: int | float = DEFAULT_smac3_max_budget,
1339 origin: SettingState = SettingState.DEFAULT) -> None:
1340 """Set the SMAC3 max budget."""
1341 section = "smac3"
1342 name = "max_budget"
1344 if self.__check_setting_state(self.__smac3_max_budget_set, origin, name):
1345 self.__init_section(section)
1346 self.__smac3_max_budget_set = origin
1347 self.__settings[section][name] = str(value)
1349 def get_smac3_max_budget(self: Settings) -> int | float:
1350 """Get the SMAC3 max budget.
1352 'The maximum budget (epochs, subset size, number of instances, ...) that
1353 is used for the optimization. Use this argument if you use multi-fidelity
1354 or instance optimization.'
1355 """
1356 if self.__smac3_max_budget_set == SettingState.NOT_SET:
1357 self.set_smac3_max_budget()
1358 return ast.literal_eval(self.__settings["smac3"]["max_budget"])
1360 # Configuration: IRACE specific settings ###
1362 def get_irace_max_time(self: Settings) -> int:
1363 """Return the max time in seconds for IRACE."""
1364 if self.__irace_max_time_set == SettingState.NOT_SET:
1365 self.set_irace_max_time()
1366 return int(self.__settings["irace"]["max_time"])
1368 def set_irace_max_time(
1369 self: Settings, value: int = DEFAULT_irace_max_time,
1370 origin: SettingState = SettingState.DEFAULT) -> None:
1371 """Set the max time in seconds for IRACE."""
1372 section = "irace"
1373 name = "max_time"
1375 if value is not None and self.__check_setting_state(
1376 self.__irace_max_time_set, origin, name):
1377 self.__init_section(section)
1378 self.__irace_max_time_set = origin
1379 self.__settings[section][name] = str(value)
1381 def get_irace_max_experiments(self: Settings) -> int:
1382 """Return the max number of experiments for IRACE."""
1383 if self.__irace_max_experiments_set == SettingState.NOT_SET:
1384 self.set_irace_max_experiments()
1385 return int(self.__settings["irace"]["max_experiments"])
1387 def set_irace_max_experiments(
1388 self: Settings, value: int = DEFAULT_irace_max_experiments,
1389 origin: SettingState = SettingState.DEFAULT) -> None:
1390 """Set the max number of experiments for IRACE."""
1391 section = "irace"
1392 name = "max_experiments"
1394 if value is not None and self.__check_setting_state(
1395 self.__irace_max_experiments_set, origin, name):
1396 self.__init_section(section)
1397 self.__irace_max_experiments_set = origin
1398 self.__settings[section][name] = str(value)
1400 def get_irace_first_test(self: Settings) -> int | None:
1401 """Return the first test for IRACE.
1403 Specifies how many instances are evaluated before the first
1404 elimination test. IRACE Default: 5. [firstTest]
1405 """
1406 if self.__irace_first_test_set == SettingState.NOT_SET:
1407 self.set_irace_first_test()
1408 first_test = self.__settings["irace"]["first_test"]
1409 return int(first_test) if first_test.isdigit() else None
1411 def set_irace_first_test(
1412 self: Settings, value: int = DEFAULT_irace_first_test,
1413 origin: SettingState = SettingState.DEFAULT) -> None:
1414 """Set the first test for IRACE."""
1415 section = "irace"
1416 name = "first_test"
1418 if self.__check_setting_state(
1419 self.__irace_first_test_set, origin, name):
1420 self.__init_section(section)
1421 self.__irace_first_test_set = origin
1422 self.__settings[section][name] = str(value)
1424 def get_irace_mu(self: Settings) -> int | None:
1425 """Return the mu for IRACE.
1427 Parameter used to define the number of configurations sampled and
1428 evaluated at each iteration. IRACE Default: 5. [mu]
1429 """
1430 if self.__irace_mu_set == SettingState.NOT_SET:
1431 self.set_irace_mu()
1432 mu = self.__settings["irace"]["mu"]
1433 return int(mu) if mu.isdigit() else None
1435 def set_irace_mu(
1436 self: Settings, value: int = DEFAULT_irace_mu,
1437 origin: SettingState = SettingState.DEFAULT) -> None:
1438 """Set the mu for IRACE."""
1439 section = "irace"
1440 name = "mu"
1442 if self.__check_setting_state(
1443 self.__irace_mu_set, origin, name):
1444 self.__init_section(section)
1445 self.__irace_mu_set = origin
1446 self.__settings[section][name] = str(value)
1448 def get_irace_max_iterations(self: Settings) -> int:
1449 """Return the number of iterations for IRACE."""
1450 if self.__irace_max_iterations_set == SettingState.NOT_SET:
1451 self.set_irace_max_iterations()
1452 max_iterations = self.__settings["irace"]["max_iterations"]
1453 return int(max_iterations) if max_iterations.isdigit() else None
1455 def set_irace_max_iterations(
1456 self: Settings, value: int = DEFAULT_irace_max_iterations,
1457 origin: SettingState = SettingState.DEFAULT) -> None:
1458 """Set the number of iterations for IRACE.
1460 Maximum number of iterations to be executed. Each iteration involves the
1461 generation of new configurations and the use of racing to select the best
1462 configurations. By default (with 0), irace calculates a minimum number of
1463 iterations as N^iter = ⌊2 + log2 N param⌋, where N^param is the number of
1464 non-fixed parameters to be tuned.
1465 Setting this parameter may make irace stop sooner than it should without using
1466 all the available budget. IRACE recommends to use the default value (Empty).
1467 """
1468 section = "irace"
1469 name = "max_iterations"
1471 if self.__check_setting_state(
1472 self.__irace_max_iterations_set, origin, name):
1473 self.__init_section(section)
1474 self.__irace_max_iterations_set = origin
1475 self.__settings[section][name] = str(value)
1477 # Configuration: ParamILS specific settings ###
1479 def get_paramils_min_runs(self: Settings) -> int | None:
1480 """Return the minimum number of runs for ParamILS."""
1481 if self.__paramils_min_runs_set == SettingState.NOT_SET:
1482 self.set_paramils_min_runs()
1483 min_runs = self.__settings["paramils"]["min_runs"]
1484 return int(min_runs) if min_runs.isdigit() else None
1486 def set_paramils_min_runs(
1487 self: Settings, value: int = DEFAULT_paramils_min_runs,
1488 origin: SettingState = SettingState.DEFAULT) -> None:
1489 """Set the minimum number of runs for ParamILS."""
1490 section = "paramils"
1491 name = "min_runs"
1493 if self.__check_setting_state(
1494 self.__paramils_min_runs_set, origin, name):
1495 self.__init_section(section)
1496 self.__paramils_min_runs_set = origin
1497 self.__settings[section][name] = str(value)
1499 def get_paramils_max_runs(self: Settings) -> int | None:
1500 """Return the maximum number of runs for ParamILS."""
1501 if self.__paramils_max_runs_set == SettingState.NOT_SET:
1502 self.set_paramils_max_runs()
1503 max_runs = self.__settings["paramils"]["min_runs"]
1504 return int(max_runs) if max_runs.isdigit() else None
1506 def set_paramils_max_runs(
1507 self: Settings, value: int = DEFAULT_paramils_max_runs,
1508 origin: SettingState = SettingState.DEFAULT) -> None:
1509 """Set the maximum number of runs for ParamILS."""
1510 section = "paramils"
1511 name = "max_runs"
1513 if self.__check_setting_state(
1514 self.__paramils_max_runs_set, origin, name):
1515 self.__init_section(section)
1516 self.__paramils_max_runs_set = origin
1517 self.__settings[section][name] = str(value)
1519 def get_paramils_tuner_timeout(self: Settings) -> int | None:
1520 """Return the maximum CPU time for ParamILS."""
1521 if self.__paramils_tuner_timeout_set == SettingState.NOT_SET:
1522 self.set_paramils_tuner_timeout()
1523 tuner_timeout = self.__settings["paramils"]["tuner_timeout"]
1524 return int(tuner_timeout) if tuner_timeout.isdigit() else None
1526 def set_paramils_tuner_timeout(
1527 self: Settings, value: int = DEFAULT_paramils_tuner_timeout,
1528 origin: SettingState = SettingState.DEFAULT) -> None:
1529 """Set the maximum CPU time for ParamILS."""
1530 section = "paramils"
1531 name = "tuner_timeout"
1533 if self.__check_setting_state(
1534 self.__paramils_tuner_timeout_set, origin, name):
1535 self.__init_section(section)
1536 self.__paramils_tuner_timeout_set = origin
1537 self.__settings[section][name] = str(value)
1539 def get_paramils_focused_approach(self: Settings) -> bool:
1540 """Return the focused approach for ParamILS."""
1541 if self.__paramils_focused_approach_set == SettingState.NOT_SET:
1542 self.set_paramils_focused_approach()
1543 return bool(self.__settings["paramils"]["focused_approach"])
1545 def set_paramils_focused_approach(
1546 self: Settings, value: bool = DEFAULT_paramils_focused_approach,
1547 origin: SettingState = SettingState.DEFAULT) -> None:
1548 """Set the focused approach for ParamILS."""
1549 section = "paramils"
1550 name = "focused_approach"
1552 if self.__check_setting_state(
1553 self.__paramils_focused_approach_set, origin, name):
1554 self.__init_section(section)
1555 self.__paramils_focused_approach_set = origin
1556 self.__settings[section][name] = str(value)
1558 def get_paramils_initial_configurations(self: Settings) -> int | None:
1559 """Return the initial configurations for ParamILS."""
1560 if self.__paramils_initial_configurations_set == SettingState.NOT_SET:
1561 self.set_paramils_initial_configurations()
1562 intial_confs = self.__settings["paramils"]["initial_configurations"]
1563 return int(intial_confs) if intial_confs.isdigit() else None
1565 def set_paramils_initial_configurations(
1566 self: Settings, value: int = DEFAULT_paramils_initial_configurations,
1567 origin: SettingState = SettingState.DEFAULT) -> None:
1568 """Set the initial configurations for ParamILS."""
1569 section = "paramils"
1570 name = "initial_configurations"
1572 if self.__check_setting_state(
1573 self.__paramils_initial_configurations_set, origin, name):
1574 self.__init_section(section)
1575 self.__paramils_initial_configurations_set = origin
1576 self.__settings[section][name] = str(value)
1578 def get_paramils_random_restart(self: Settings) -> float | None:
1579 """Return the random restart chance for ParamILS."""
1580 if self.__paramils_random_restart_set == SettingState.NOT_SET:
1581 self.set_paramils_random_restart()
1582 return ast.literal_eval(self.__settings["paramils"]["random_restart"])
1584 def set_paramils_random_restart(
1585 self: Settings, value: float = DEFAULT_paramils_random_restart,
1586 origin: SettingState = SettingState.DEFAULT) -> None:
1587 """Set the random restart chance for ParamILS."""
1588 section = "paramils"
1589 name = "random_restart"
1591 if self.__check_setting_state(
1592 self.__paramils_random_restart_set, origin, name):
1593 self.__init_section(section)
1594 self.__paramils_random_restart_set = origin
1595 self.__settings[section][name] = str(value)
1597 def set_paramils_use_cpu_time_in_tunertime(
1598 self: Settings, value: bool = DEFAULT_paramils_use_cpu_time_in_tunertime,
1599 origin: SettingState = SettingState.DEFAULT) -> None:
1600 """Set whether to use CPU time in tunertime."""
1601 section = "paramils"
1602 name = "use_cpu_time_in_tunertime"
1604 if self.__check_setting_state(
1605 self.__paramils_use_cpu_time_in_tunertime_set, origin, name):
1606 self.__init_section(section)
1607 self.__paramils_use_cpu_time_in_tunertime_set = origin
1608 self.__settings[section][name] = str(value)
1610 def get_paramils_use_cpu_time_in_tunertime(self: Settings) -> bool:
1611 """Return whether to use CPU time in tunertime."""
1612 if self.__paramils_use_cpu_time_in_tunertime_set == SettingState.NOT_SET:
1613 self.set_paramils_use_cpu_time_in_tunertime()
1614 return ast.literal_eval(self.__settings["paramils"]["use_cpu_time_in_tunertime"])
1616 def set_paramils_cli_cores(
1617 self: Settings, value: int = DEFAULT_paramils_cli_cores,
1618 origin: SettingState = SettingState.DEFAULT) -> None:
1619 """Set the number of cores to use for ParamILS CLI."""
1620 section = "paramils"
1621 name = "cli_cores"
1623 if self.__check_setting_state(
1624 self.__paramils_cli_cores_set, origin, name):
1625 self.__init_section(section)
1626 self.__paramils_cli_cores_set = origin
1627 self.__settings[section][name] = str(value)
1629 def get_paramils_cli_cores(self: Settings) -> int | None:
1630 """Number of cores to use to execute runs.
1632 In other words, the number of requests to run at a given time.
1633 """
1634 if self.__paramils_cli_cores_set == SettingState.NOT_SET:
1635 self.set_paramils_cli_cores()
1636 cli_cores = self.__settings["paramils"]["cli_cores"]
1637 return int(cli_cores) if cli_cores.isdigit() else None
1639 def set_paramils_max_iterations(
1640 self: Settings, value: int = DEFAULT_paramils_max_iterations,
1641 origin: SettingState = SettingState.DEFAULT) -> None:
1642 """Set the maximum number of ParamILS iterations."""
1643 section = "paramils"
1644 name = "max_iterations"
1646 if self.__check_setting_state(
1647 self.__paramils_max_iterations_set, origin, name):
1648 self.__init_section(section)
1649 self.__paramils_max_iterations_set = origin
1650 self.__settings[section][name] = str(value)
1652 def get_paramils_max_iterations(self: Settings) -> int | None:
1653 """Get the maximum number of paramils iterations."""
1654 if self.__smac2_max_iterations_set == SettingState.NOT_SET:
1655 self.set_paramils_max_iterations()
1656 max_iterations = self.__settings["paramils"]["max_iterations"]
1657 return int(max_iterations) if max_iterations.isdigit() else None
1659 # Selection settings ###
1661 def set_selection_class(
1662 self: Settings,
1663 value: str = DEFAULT_selector_class,
1664 origin: SettingState = SettingState.DEFAULT) -> None:
1665 """Set the Sparkle selector.
1667 Can contain any of the class names as defined in asf.selectors.
1668 """
1669 section = "selection"
1670 name = "selector_class"
1671 if value is not None and self.__check_setting_state(
1672 self.__selection_class_set, origin, name):
1673 self.__init_section(section)
1674 self.__selection_class_set = origin
1675 self.__settings[section][name] = str(value)
1677 def get_selection_class(self: Settings) -> type:
1678 """Return the selector class."""
1679 if self.__selection_class_set == SettingState.NOT_SET:
1680 self.set_selection_class()
1681 from asf import selectors
1682 return getattr(selectors, self.__settings["selection"]["selector_class"])
1684 def set_selection_model(
1685 self: Settings,
1686 value: str = DEFAULT_selector_model,
1687 origin: SettingState = SettingState.DEFAULT) -> None:
1688 """Set the selector model.
1690 Can be any of the sklearn.ensemble models.
1691 """
1692 section = "selection"
1693 name = "selector_model"
1694 if value is not None and self.__check_setting_state(
1695 self.__selection_model_set, origin, name):
1696 self.__init_section(section)
1697 self.__selection_model_set = origin
1698 self.__settings[section][name] = str(value)
1700 def get_selection_model(self: Settings) -> type:
1701 """Return the selector model class."""
1702 if self.__selection_model_set == SettingState.NOT_SET:
1703 self.set_selection_model()
1704 from sklearn import ensemble
1705 return getattr(ensemble, self.__settings["selection"]["selector_model"])
1707 # Slurm settings ###
1709 def set_slurm_max_parallel_runs_per_node(
1710 self: Settings,
1711 value: int = DEFAULT_slurm_max_parallel_runs_per_node,
1712 origin: SettingState = SettingState.DEFAULT) -> None:
1713 """Set the number of algorithms Slurm can run in parallel per node."""
1714 section = "slurm"
1715 name = "max_parallel_runs_per_node"
1717 if value is not None and self.__check_setting_state(
1718 self.__slurm_max_parallel_runs_per_node_set, origin, name):
1719 self.__init_section(section)
1720 self.__slurm_max_parallel_runs_per_node_set = origin
1721 self.__settings[section][name] = str(value)
1723 def get_slurm_max_parallel_runs_per_node(self: Settings) -> int:
1724 """Return the number of algorithms Slurm can run in parallel per node."""
1725 if self.__slurm_max_parallel_runs_per_node_set == SettingState.NOT_SET:
1726 self.set_slurm_max_parallel_runs_per_node()
1728 return int(self.__settings["slurm"]["max_parallel_runs_per_node"])
1730 def set_slurm_job_submission_limit(
1731 self: Settings,
1732 value: int = DEFAULT_slurm_job_submission_limit,
1733 origin: SettingState = SettingState.DEFAULT) -> None:
1734 """[NOT ACTIVE YET] Set the number of jobs that can be submitted to Slurm."""
1735 section = "slurm"
1736 name = "job_submission_limit"
1738 if value is not None and self.__check_setting_state(
1739 self.__slurm_job_submission_limit_set, origin, name):
1740 self.__init_section(section)
1741 self.__slurm_job_submission_limit_set = origin
1742 self.__settings[section][name] = str(value)
1744 def get_slurm_job_submission_limit(self: Settings) -> int:
1745 """[NOT ACTIVE YET] Return the maximum number of jobs you can submit to Slurm."""
1746 if self.__slurm_job_submission_limit_set == SettingState.NOT_SET:
1747 self.set_slurm_job_submission_limit()
1749 return int(self.__settings["slurm"]["job_submission_limit"])
1751 def set_slurm_job_prepend(
1752 self: Settings,
1753 value: str = DEFAULT_slurm_job_prepend,
1754 origin: SettingState = SettingState.DEFAULT) -> None:
1755 """Set the Slurm job prepend."""
1756 section = "slurm"
1757 name = "job_prepend"
1759 if self.__check_setting_state(
1760 self.__slurm_job_prepend_set, origin, name):
1761 try:
1762 path = Path(value)
1763 if path.is_file():
1764 with path.open() as f:
1765 value = f.read()
1766 f.close()
1767 except TypeError:
1768 pass
1769 self.__init_section(section)
1770 self.__slurm_job_prepend_set = origin
1771 self.__settings[section][name] = str(value)
1773 def get_slurm_job_prepend(self: Settings) -> str:
1774 """Return the Slurm job prepend."""
1775 if self.__slurm_job_prepend_set == SettingState.NOT_SET:
1776 self.set_slurm_job_prepend()
1778 return self.__settings["slurm"]["job_prepend"]
1780 # SLURM extra options
1782 def add_slurm_extra_option(self: Settings, name: str, value: str,
1783 origin: SettingState = SettingState.DEFAULT) -> None:
1784 """Add additional Slurm options."""
1785 section = "slurm_extra"
1787 current_state = (self.__slurm_extra_options_set[name]
1788 if name in self.__slurm_extra_options_set
1789 else SettingState.NOT_SET)
1791 if value is not None and self.__check_setting_state(current_state, origin, name):
1792 self.__init_section(section)
1793 self.__slurm_extra_options_set[name] = origin
1794 self.__settings[section][name] = str(value)
1796 def get_slurm_extra_options(self: Settings,
1797 as_args: bool = False) -> dict | list:
1798 """Return a dict with additional Slurm options."""
1799 section = "slurm_extra"
1800 options = dict()
1802 if "slurm_extra" in self.__settings.sections():
1803 for option in self.__settings["slurm_extra"]:
1804 options[option] = self.__settings.get(section, option)
1805 if as_args:
1806 return [f"--{key}={options[key]}" for key in options.keys()]
1807 return options
1809 # Ablation settings ###
1811 def set_ablation_racing_flag(self: Settings, value: bool = DEFAULT_ablation_racing,
1812 origin: SettingState = SettingState.DEFAULT) -> None:
1813 """Set a flag indicating whether racing should be used for ablation."""
1814 section = "ablation"
1815 name = "racing"
1817 if value is not None and self.__check_setting_state(
1818 self.__ablation_racing_flag_set, origin, name):
1819 self.__init_section(section)
1820 self.__ablation_racing_flag_set = origin
1821 self.__settings[section][name] = str(value)
1823 def get_ablation_racing_flag(self: Settings) -> bool:
1824 """Return a bool indicating whether the racing flag is set for ablation."""
1825 if self.__ablation_racing_flag_set == SettingState.NOT_SET:
1826 self.set_ablation_racing_flag()
1828 return bool(self.__settings["ablation"]["racing"])
1830 # Parallel Portfolio settings
1832 def set_parallel_portfolio_check_interval(
1833 self: Settings,
1834 value: int = DEFAULT_parallel_portfolio_check_interval,
1835 origin: SettingState = SettingState.DEFAULT) -> None:
1836 """Set the parallel portfolio check interval."""
1837 section = "parallel_portfolio"
1838 name = "check_interval"
1840 if value is not None and self.__check_setting_state(
1841 self.__parallel_portfolio_check_interval_set, origin, name):
1842 self.__init_section(section)
1843 self.__parallel_portfolio_check_interval_set = origin
1844 self.__settings[section][name] = str(value)
1846 def get_parallel_portfolio_check_interval(self: Settings) -> int:
1847 """Return the parallel portfolio check interval."""
1848 if self.__parallel_portfolio_check_interval_set == SettingState.NOT_SET:
1849 self.set_parallel_portfolio_check_interval()
1851 return int(
1852 self.__settings["parallel_portfolio"]["check_interval"])
1854 def set_parallel_portfolio_number_of_seeds_per_solver(
1855 self: Settings,
1856 value: int = DEFAULT_parallel_portfolio_num_seeds_per_solver,
1857 origin: SettingState = SettingState.DEFAULT) -> None:
1858 """Set the parallel portfolio seeds per solver to start."""
1859 section = "parallel_portfolio"
1860 name = "num_seeds_per_solver"
1862 if value is not None and self.__check_setting_state(
1863 self.__parallel_portfolio_num_seeds_per_solver_set, origin, name):
1864 self.__init_section(section)
1865 self.__parallel_portfolio_num_seeds_per_solver_set = origin
1866 self.__settings[section][name] = str(value)
1868 def get_parallel_portfolio_number_of_seeds_per_solver(self: Settings) -> int:
1869 """Return the parallel portfolio seeds per solver to start."""
1870 if self.__parallel_portfolio_num_seeds_per_solver_set == SettingState.NOT_SET:
1871 self.set_parallel_portfolio_number_of_seeds_per_solver()
1873 return int(
1874 self.__settings["parallel_portfolio"]["num_seeds_per_solver"])
1876 def set_run_on(self: Settings, value: Runner = DEFAULT_general_run_on,
1877 origin: SettingState = SettingState.DEFAULT) -> None:
1878 """Set the compute on which to run."""
1879 section = "general"
1880 name = "run_on"
1882 if value is not None and self.__check_setting_state(
1883 self.__run_on_set, origin, name):
1884 self.__init_section(section)
1885 self.__run_on_set = origin
1886 self.__settings[section][name] = value
1888 def get_run_on(self: Settings) -> Runner:
1889 """Return the compute on which to run."""
1890 if self.__run_on_set == SettingState.NOT_SET:
1891 self.set_run_on()
1893 return Runner(self.__settings["general"]["run_on"])
1895 @staticmethod
1896 def check_settings_changes(cur_settings: Settings, prev_settings: Settings) -> bool:
1897 """Check if there are changes between the previous and the current settings.
1899 Prints any section changes, printing None if no setting was found.
1901 Args:
1902 cur_settings: The current settings
1903 prev_settings: The previous settings
1905 Returns:
1906 True iff there are no changes.
1907 """
1908 cur_dict = cur_settings.__settings._sections
1909 prev_dict = prev_settings.__settings._sections
1911 cur_sections_set = set(cur_dict.keys())
1912 prev_sections_set = set(prev_dict.keys())
1913 sections_removed = prev_sections_set - cur_sections_set
1914 if sections_removed:
1915 print("[INFO] The following sections have been removed:")
1916 for section in sections_removed:
1917 print(f" - Section '{section}'")
1919 sections_added = cur_sections_set - prev_sections_set
1920 if sections_added:
1921 print("[INFO] The following sections have been added:")
1922 for section in sections_added:
1923 print(f" - Section '{section}'")
1925 sections_remained = cur_sections_set & prev_sections_set
1926 option_changed = False
1927 for section in sections_remained:
1928 printed_section = False
1929 names = set(cur_dict[section].keys()) | set(prev_dict[section].keys())
1930 for name in names:
1931 # if name is not present in one of the two dicts, get None as placeholder
1932 cur_val = cur_dict[section].get(name, None)
1933 prev_val = prev_dict[section].get(name, None)
1935 # If cur val is None, it is default
1936 if cur_val is not None and cur_val != prev_val:
1937 # Have we printed the initial warning?
1938 if not option_changed:
1939 print("[INFO] The following attributes/options have changed:")
1940 option_changed = True
1942 # do we have yet to print the section?
1943 if not printed_section:
1944 print(f" - In the section '{section}':")
1945 printed_section = True
1947 # print actual change
1948 print(f" · '{name}' changed from '{prev_val}' to '{cur_val}'")
1950 return not (sections_removed or sections_added or option_changed)