Coverage for sparkle/configurator/configurator.py: 82%
62 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-07 15:22 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-07 15:22 +0000
1#!/usr/bin/env python3
2# -*- coding: UTF-8 -*-
3"""Configurator class to use different algorithm configurators like SMAC."""
4from __future__ import annotations
5from pathlib import Path
7import runrunner as rrr
8from runrunner import Runner, Run
10from sparkle.solver import Solver
11from sparkle.instance import InstanceSet
12from sparkle.structures import PerformanceDataFrame
13from sparkle.types import SparkleObjective
16class Configurator:
17 """Abstact class to use different configurators like SMAC."""
18 configurator_cli_path = Path(__file__).parent.resolve() / "configurator_cli.py"
20 def __init__(self: Configurator, output_path: Path,
21 base_dir: Path, tmp_path: Path,
22 multi_objective_support: bool = False) -> None:
23 """Initialize Configurator.
25 Args:
26 output_path: Output directory of the Configurator.
27 objectives: The list of Sparkle Objectives the configurator has to
28 optimize.
29 base_dir: Where to execute the configuration
30 tmp_path: Path for the temporary files of the configurator, optional
31 multi_objective_support: Whether the configurator supports
32 multi objective optimization for solvers.
33 """
34 self.output_path = output_path
35 self.base_dir = base_dir
36 self.tmp_path = tmp_path
37 self.multiobjective = multi_objective_support
38 self.scenario = None
40 @staticmethod
41 def scenario_class() -> ConfigurationScenario:
42 """Return the scenario class of the configurator."""
43 return ConfigurationScenario
45 def configure(self: Configurator,
46 configuration_commands: list[str],
47 data_target: PerformanceDataFrame,
48 output: Path,
49 scenario: ConfigurationScenario,
50 validation_ids: list[int] = None,
51 sbatch_options: list[str] = None,
52 num_parallel_jobs: int = None,
53 base_dir: Path = None,
54 run_on: Runner = Runner.SLURM) -> Run:
55 """Start configuration job.
57 Args:
59 scenario: ConfigurationScenario to execute.
60 validate_after: Whether to validate the configuration on the training set
61 afterwards or not.
62 sbatch_options: List of slurm batch options to use
63 num_parallel_jobs: The maximum number of jobs to run in parallel
64 base_dir: The base_dir of RunRunner where the sbatch scripts will be placed
65 run_on: On which platform to run the jobs. Default: Slurm.
67 Returns:
68 A RunRunner Run object.
69 """
70 runs = [rrr.add_to_queue(
71 runner=run_on,
72 cmd=configuration_commands,
73 name=f"{self.name}: {scenario.solver.name} on {scenario.instance_set.name}",
74 base_dir=base_dir,
75 output_path=output,
76 parallel_jobs=num_parallel_jobs,
77 sbatch_options=sbatch_options)]
79 if validation_ids:
80 validate = scenario.solver.run_performance_dataframe(
81 scenario.instance_set,
82 run_ids=validation_ids,
83 performance_dataframe=data_target,
84 cutoff_time=scenario.cutoff_time,
85 sbatch_options=sbatch_options,
86 log_dir=scenario.validation,
87 base_dir=base_dir,
88 dependencies=runs,
89 job_name=f"{self.name}: Validating {len(validation_ids)} "
90 f"{scenario.solver.name} Configurations on "
91 f"{scenario.instance_set.name}",
92 run_on=run_on,
93 )
94 runs.append(validate)
96 if run_on == Runner.LOCAL:
97 for run in runs:
98 run.wait()
99 return runs
101 @staticmethod
102 def organise_output(output_source: Path,
103 output_target: Path,
104 scenario: ConfigurationScenario,
105 run_id: int) -> None | str:
106 """Method to restructure and clean up after a single configurator call.
108 Args:
109 output_source: Path to the output file of the configurator run.
110 output_target: Path to the Performance DataFrame to store result.
111 scenario: ConfigurationScenario of the configuration.
112 run_id: ID of the run of the configuration.
113 """
114 raise NotImplementedError
116 def get_status_from_logs(self: Configurator) -> None:
117 """Method to scan the log files of the configurator for warnings."""
118 raise NotImplementedError
121class ConfigurationScenario:
122 """Template class to handle a configuration scenarios."""
123 def __init__(self: ConfigurationScenario,
124 solver: Solver,
125 instance_set: InstanceSet,
126 sparkle_objectives: list[SparkleObjective],
127 parent_directory: Path) -> None:
128 """Initialize scenario paths and names.
130 Args:
131 solver: Solver that should be configured.
132 instance_set: Instances object for the scenario.
133 sparkle_objectives: Sparkle Objectives to optimize.
134 parent_directory: Directory in which the scenario should be placed.
135 """
136 self.solver = solver
137 self.instance_set = instance_set
138 self.sparkle_objectives = sparkle_objectives
139 self.name = f"{self.solver.name}_{self.instance_set.name}"
141 if self.instance_set.size == 0:
142 raise Exception("Cannot configure on an empty instance set "
143 f"('{instance_set.name}').")
145 self.directory = parent_directory / self.name
146 self.scenario_file_path = self.directory / f"{self.name}_scenario.txt"
147 self.validation: Path = self.directory / "validation"
148 self.tmp: Path = self.directory / "tmp"
149 self.results_directory: Path = self.directory / "results"
151 def create_scenario(self: ConfigurationScenario, parent_directory: Path) -> None:
152 """Create scenario with solver and instances in the parent directory.
154 This prepares all the necessary subdirectories related to configuration.
156 Args:
157 parent_directory: Directory in which the scenario should be created.
158 """
159 raise NotImplementedError
161 def create_scenario_file(self: ConfigurationScenario) -> Path:
162 """Create a file with the configuration scenario.
164 Writes supplementary information to the target algorithm (algo =) as:
165 algo = {configurator_target} {solver_directory} {sparkle_objective}
166 """
167 raise NotImplementedError
169 def serialize(self: ConfigurationScenario) -> dict:
170 """Serialize the configuration scenario."""
171 raise NotImplementedError
173 @classmethod
174 def find_scenario(cls: ConfigurationScenario,
175 directory: Path,
176 solver: Solver,
177 instance_set: InstanceSet) -> ConfigurationScenario:
178 """Resolve a scenario from a directory and Solver / Training set."""
179 scenario_name = f"{solver.name}_{instance_set.name}"
180 path = directory / f"{scenario_name}" / f"{scenario_name}_scenario.txt"
181 if not path.exists():
182 return None
183 return cls.from_file(path)
185 @staticmethod
186 def from_file(scenario_file: Path) -> ConfigurationScenario:
187 """Reads scenario file and initalises ConfigurationScenario."""
188 raise NotImplementedError