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

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 

8 

9from runrunner import Runner 

10 

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 

16 

17 

18class SettingState(Enum): 

19 """Enum of possible setting states.""" 

20 

21 NOT_SET = 0 

22 DEFAULT = 1 

23 FILE = 2 

24 CMD_LINE = 3 

25 

26 

27class Settings: 

28 """Class to read, write, set, and get settings.""" 

29 # CWD Prefix 

30 cwd_prefix = Path() # Empty for now 

31 

32 # Library prefix 

33 lib_prefix = Path(__file__).parent.parent.resolve() 

34 

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") 

41 

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" 

47 

48 # Default library pathing 

49 DEFAULT_components = lib_prefix / "Components" 

50 

51 # Report Component: Bilbiography 

52 bibliography_path = DEFAULT_components / "latex_source" / "report.bib" 

53 

54 # Example settings path 

55 DEFAULT_example_settings_path = PurePath(DEFAULT_components / "sparkle_settings.ini") 

56 

57 # Runsolver component 

58 DEFAULT_runsolver_dir = DEFAULT_components / "runsolver" / "src" 

59 DEFAULT_runsolver_exec = DEFAULT_runsolver_dir / "runsolver" 

60 

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" 

65 

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" 

71 

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" 

80 

81 # Default output subdirs 

82 DEFAULT_output_analysis = DEFAULT_output / analysis_dir 

83 

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" 

87 

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 ] 

98 

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" 

104 

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" 

114 

115 DEFAULT_configurator_number_of_runs = 25 

116 DEFAULT_configurator_solver_calls = 100 

117 DEFAULT_configurator_maximum_iterations = None 

118 

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 

126 

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 

138 

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 

145 

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 

157 

158 DEFAULT_slurm_max_parallel_runs_per_node = 8 

159 DEFAULT_slurm_job_submission_limit = None 

160 DEFAULT_slurm_job_prepend = "" 

161 

162 DEFAULT_ablation_racing = False 

163 

164 DEFAULT_parallel_portfolio_check_interval = 4 

165 DEFAULT_parallel_portfolio_num_seeds_per_solver = 1 

166 

167 # Default selection settings 

168 DEFAULT_selector_class = "MultiClassClassifier" 

169 DEFAULT_selector_model = "RandomForestClassifier" 

170 

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() 

175 

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 

183 

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 

187 

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 

194 

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 

205 

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 

211 

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 

221 

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 

225 

226 self.__parallel_portfolio_check_interval_set = SettingState.NOT_SET 

227 self.__parallel_portfolio_num_seeds_per_solver_set = SettingState.NOT_SET 

228 

229 self.__selection_model_set = SettingState.NOT_SET 

230 self.__selection_class_set = SettingState.NOT_SET 

231 

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 

235 

236 self.__general_sparkle_configurator = None 

237 

238 self.__slurm_extra_options_set = dict() 

239 

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) 

246 

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) 

253 

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) 

265 

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) 

273 

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) 

281 

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) 

289 

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) 

296 

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) 

304 

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) 

311 

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) 

319 

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) 

326 

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) 

332 

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) 

340 

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) 

347 

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) 

354 

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) 

361 

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) 

368 

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) 

376 

377 section = "smac3" 

378 

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) 

385 

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) 

392 

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) 

399 

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) 

406 

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) 

413 

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) 

420 

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) 

427 

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) 

434 

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) 

441 

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) 

448 

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) 

456 

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) 

463 

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) 

470 

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) 

477 

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) 

484 

485 section = "paramils" 

486 

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) 

493 

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) 

500 

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) 

508 

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) 

515 

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) 

522 

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) 

529 

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) 

536 

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) 

544 

545 section = "selection" 

546 

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) 

553 

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) 

560 

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) 

568 

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) 

575 

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) 

582 

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) 

589 

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) 

597 

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) 

605 

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) 

612 

613 # TODO: Report on any unknown settings that were read 

614 sections = file_settings.sections() 

615 

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') 

625 

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.") 

631 

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") 

636 

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" 

670 

671 def __init_section(self: Settings, section: str) -> None: 

672 if section not in self.__settings: 

673 self.__settings[section] = {} 

674 

675 @staticmethod 

676 def __check_setting_state(current_state: SettingState, 

677 new_state: SettingState, name: str) -> bool: 

678 change_setting_ok = True 

679 

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!") 

693 

694 return change_setting_ok 

695 

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" 

704 

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) 

711 

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" 

721 

722 self.__init_section(section) 

723 self.__general_sparkle_objective_set = origin 

724 self.__settings[section][name] = value 

725 

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() 

732 

733 objectives = [resolve_objective(obj) 

734 for obj in self.__settings["general"]["objectives"].split(",")] 

735 

736 if filter_metric: 

737 return [obj for obj in objectives if not obj.metric] 

738 

739 return objectives 

740 

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 

753 

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 

770 

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 

774 

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) 

786 

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"]) 

792 

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" 

799 

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) 

805 

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"]) 

811 

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" 

818 

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) 

824 

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() 

829 

830 return int(self.__settings["slurm"]["number_of_jobs_in_parallel"]) 

831 

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" 

838 

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 

844 

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() 

849 

850 return VerbosityLevel.from_string( 

851 self.__settings["general"]["verbosity"]) 

852 

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" 

860 

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) 

866 

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() 

871 

872 return int(self.__settings["general"]["check_interval"]) 

873 

874 # Configuration settings General ### 

875 

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 

947 

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" 

954 

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) 

960 

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() 

965 

966 return int(self.__settings["configuration"]["solver_calls"]) 

967 

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" 

974 

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) 

980 

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() 

985 

986 return int(self.__settings["configuration"]["number_of_runs"]) 

987 

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" 

994 

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) 

1000 

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 

1007 

1008 # Configuration: SMAC2 specific settings ### 

1009 

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" 

1016 

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) 

1022 

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 

1029 

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" 

1036 

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) 

1042 

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 

1049 

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" 

1056 

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) 

1062 

1063 def get_smac2_target_cutoff_length(self: Settings) -> str: 

1064 """Return the target algorithm cutoff length. 

1065 

1066 'A domain specific measure of when the algorithm should consider itself done.' 

1067 

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"] 

1074 

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" 

1081 

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) 

1087 

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"]) 

1093 

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" 

1100 

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) 

1106 

1107 def get_smac2_cli_cores(self: Settings) -> int | None: 

1108 """Number of cores to use to execute runs. 

1109 

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 

1116 

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" 

1123 

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) 

1129 

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 

1136 

1137 # Configuration: SMAC3 specific settings ### 

1138 

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" 

1145 

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) 

1151 

1152 def get_smac3_number_of_trials(self: Settings) -> int | None: 

1153 """Return the number of SMAC3 trials (Solver calls). 

1154 

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 

1162 

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" 

1169 

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) 

1174 

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"] 

1180 

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" 

1187 

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) 

1193 

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"]) 

1199 

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" 

1205 

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) 

1210 

1211 def get_smac3_crash_cost(self: Settings) -> float | list[float]: 

1212 """Get the SMAC3 objective crash cost. 

1213 

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"]) 

1221 

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" 

1229 

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) 

1235 

1236 def get_smac3_termination_cost_threshold(self: Settings) -> float | list[float]: 

1237 """Get the SMAC3 termination cost threshold. 

1238 

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"]) 

1246 

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" 

1253 

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) 

1258 

1259 def get_smac3_walltime_limit(self: Settings) -> float: 

1260 """Get the SMAC3 walltime limit. 

1261 

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"]) 

1267 

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" 

1274 

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) 

1279 

1280 def get_smac3_cputime_limit(self: Settings) -> float: 

1281 """Get the SMAC3 CPU time limit. 

1282 

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"]) 

1288 

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" 

1295 

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) 

1300 

1301 def get_smac3_use_default_config(self: Settings) -> bool: 

1302 """Get the SMAC3 to use default config. 

1303 

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"]) 

1313 

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" 

1320 

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) 

1325 

1326 def get_smac3_min_budget(self: Settings) -> int | float: 

1327 """Get the SMAC3 min budget. 

1328 

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"]) 

1336 

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" 

1343 

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) 

1348 

1349 def get_smac3_max_budget(self: Settings) -> int | float: 

1350 """Get the SMAC3 max budget. 

1351 

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"]) 

1359 

1360 # Configuration: IRACE specific settings ### 

1361 

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"]) 

1367 

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" 

1374 

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) 

1380 

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"]) 

1386 

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" 

1393 

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) 

1399 

1400 def get_irace_first_test(self: Settings) -> int | None: 

1401 """Return the first test for IRACE. 

1402 

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 

1410 

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" 

1417 

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) 

1423 

1424 def get_irace_mu(self: Settings) -> int | None: 

1425 """Return the mu for IRACE. 

1426 

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 

1434 

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" 

1441 

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) 

1447 

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 

1454 

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. 

1459 

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" 

1470 

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) 

1476 

1477 # Configuration: ParamILS specific settings ### 

1478 

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 

1485 

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" 

1492 

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) 

1498 

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 

1505 

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" 

1512 

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) 

1518 

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 

1525 

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" 

1532 

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) 

1538 

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"]) 

1544 

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" 

1551 

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) 

1557 

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 

1564 

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" 

1571 

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) 

1577 

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"]) 

1583 

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" 

1590 

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) 

1596 

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" 

1603 

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) 

1609 

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"]) 

1615 

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" 

1622 

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) 

1628 

1629 def get_paramils_cli_cores(self: Settings) -> int | None: 

1630 """Number of cores to use to execute runs. 

1631 

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 

1638 

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" 

1645 

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) 

1651 

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 

1658 

1659 # Selection settings ### 

1660 

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. 

1666 

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) 

1676 

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"]) 

1683 

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. 

1689 

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) 

1699 

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"]) 

1706 

1707 # Slurm settings ### 

1708 

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" 

1716 

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) 

1722 

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() 

1727 

1728 return int(self.__settings["slurm"]["max_parallel_runs_per_node"]) 

1729 

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" 

1737 

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) 

1743 

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() 

1748 

1749 return int(self.__settings["slurm"]["job_submission_limit"]) 

1750 

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" 

1758 

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) 

1772 

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() 

1777 

1778 return self.__settings["slurm"]["job_prepend"] 

1779 

1780 # SLURM extra options 

1781 

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" 

1786 

1787 current_state = (self.__slurm_extra_options_set[name] 

1788 if name in self.__slurm_extra_options_set 

1789 else SettingState.NOT_SET) 

1790 

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) 

1795 

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() 

1801 

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 

1808 

1809 # Ablation settings ### 

1810 

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" 

1816 

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) 

1822 

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() 

1827 

1828 return bool(self.__settings["ablation"]["racing"]) 

1829 

1830 # Parallel Portfolio settings 

1831 

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" 

1839 

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) 

1845 

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() 

1850 

1851 return int( 

1852 self.__settings["parallel_portfolio"]["check_interval"]) 

1853 

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" 

1861 

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) 

1867 

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() 

1872 

1873 return int( 

1874 self.__settings["parallel_portfolio"]["num_seeds_per_solver"]) 

1875 

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" 

1881 

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 

1887 

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() 

1892 

1893 return Runner(self.__settings["general"]["run_on"]) 

1894 

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. 

1898 

1899 Prints any section changes, printing None if no setting was found. 

1900 

1901 Args: 

1902 cur_settings: The current settings 

1903 prev_settings: The previous settings 

1904 

1905 Returns: 

1906 True iff there are no changes. 

1907 """ 

1908 cur_dict = cur_settings.__settings._sections 

1909 prev_dict = prev_settings.__settings._sections 

1910 

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}'") 

1918 

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}'") 

1924 

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) 

1934 

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 

1941 

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 

1946 

1947 # print actual change 

1948 print(f" · '{name}' changed from '{prev_val}' to '{cur_val}'") 

1949 

1950 return not (sections_removed or sections_added or option_changed)