Coverage for sparkle/CLI/run_portfolio_selector.py: 87%
85 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-05 14:48 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-05 14:48 +0000
1#!/usr/bin/env python3
2"""Sparkle command to execute a portfolio selector."""
4import sys
5import argparse
6from pathlib import PurePath, Path
8import runrunner as rrr
9from runrunner import Runner
11from sparkle.CLI.help import global_variables as gv
12from sparkle.CLI.help import logging as sl
13from sparkle.platform.settings_objects import Settings, SettingState
14from sparkle.CLI.help import argparse_custom as ac
15from sparkle.structures import PerformanceDataFrame, FeatureDataFrame
16from sparkle.platform import CommandName, COMMAND_DEPENDENCIES
17from sparkle.CLI.help.reporting_scenario import Scenario
18from sparkle.CLI.initialise import check_for_initialise
19from sparkle.CLI.help.nicknames import resolve_object_name
20from sparkle.instance import Instance_Set
21from sparkle.CLI.compute_features import compute_features
24def parser_function() -> argparse.ArgumentParser:
25 """Define the command line arguments."""
26 parser = argparse.ArgumentParser(
27 description="Run a portfolio selector on instance (set), determine which solver "
28 "is most likely to perform well and run it on the instance (set).")
29 parser.add_argument(*ac.InstancePathPositional.names,
30 **ac.InstancePathPositional.kwargs)
31 parser.add_argument(*ac.RunOnArgument.names,
32 **ac.RunOnArgument.kwargs)
33 parser.add_argument(*ac.SettingsFileArgument.names,
34 **ac.SettingsFileArgument.kwargs)
35 parser.add_argument(*ac.SparkleObjectiveArgument.names,
36 **ac.SparkleObjectiveArgument.kwargs)
38 return parser
41def main(argv: list[str]) -> None:
42 """Main function of the run portfolio selector command."""
43 # Log command call
44 sl.log_command(sys.argv)
46 # Define command line arguments
47 parser = parser_function()
49 # Process command line arguments
50 args = parser.parse_args(argv)
52 check_for_initialise(COMMAND_DEPENDENCIES[CommandName.RUN_PORTFOLIO_SELECTOR])
54 if ac.set_by_user(args, "settings_file"):
55 gv.settings().read_settings_ini(
56 args.settings_file, SettingState.CMD_LINE
57 ) # Do first, so other command line options can override settings from the file
58 if ac.set_by_user(args, "objectives"):
59 gv.settings().set_general_sparkle_objectives(args.objectives,
60 SettingState.CMD_LINE)
61 if args.run_on is not None:
62 gv.settings().set_run_on(args.run_on.value, SettingState.CMD_LINE)
64 # Compare current settings to latest.ini
65 prev_settings = Settings(PurePath("Settings/latest.ini"))
66 Settings.check_settings_changes(gv.settings(), prev_settings)
68 data_set = resolve_object_name(
69 args.instance_path,
70 gv.file_storage_data_mapping[gv.instances_nickname_path],
71 gv.settings().DEFAULT_instance_dir, Instance_Set)
73 if data_set is None:
74 print("ERROR: The instance (set) could not be found. Please make sure the "
75 "path is correct.")
76 sys.exit(-1)
78 run_on = gv.settings().get_run_on()
79 objectives = gv.settings().get_general_sparkle_objectives()
80 # NOTE: Is this still relevant?
81 if not objectives[0].time:
82 print("ERROR: The run_portfolio_selector command is not yet implemented"
83 " for the QUALITY_ABSOLUTE performance measure!")
84 sys.exit(-1)
86 selector_scenario = gv.latest_scenario().get_selection_scenario_path()
87 selector_path = selector_scenario / "portfolio_selector"
88 if not selector_path.exists() or not selector_path.is_file():
89 print("ERROR: The portfolio selector could not be found. Please make sure to "
90 "first construct a portfolio selector.")
91 sys.exit(-1)
92 if len([p for p in gv.settings().DEFAULT_extractor_dir.iterdir()]) == 0:
93 print("ERROR: No feature extractor added to Sparkle.")
94 sys.exit(-1)
96 # Compute the features of the incoming instances
97 test_case_path = selector_scenario / data_set.name
98 test_case_path.mkdir(exist_ok=True)
99 feature_dataframe = FeatureDataFrame(gv.settings().DEFAULT_feature_data_path)
100 feature_dataframe.remove_instances(feature_dataframe.instances)
101 feature_dataframe.csv_filepath = test_case_path / "feature_data.csv"
102 feature_dataframe.add_instances(data_set.instance_paths)
103 feature_dataframe.save_csv()
104 feature_run = compute_features(feature_dataframe, recompute=False, run_on=run_on)
106 if run_on == Runner.LOCAL:
107 feature_run.wait()
109 # Prepare performance data
110 performance_data = PerformanceDataFrame(
111 test_case_path / "performance_data.csv",
112 objectives=objectives)
113 for instance_name in data_set.instance_names:
114 if instance_name not in performance_data.instances:
115 performance_data.add_instance(instance_name)
116 performance_data.add_solver(selector_path.name)
117 performance_data.save_csv()
118 # Update latest scenario
119 gv.latest_scenario().set_selection_test_case_directory(test_case_path)
120 gv.latest_scenario().set_latest_scenario(Scenario.SELECTION)
121 # Write used scenario to file
122 gv.latest_scenario().write_scenario_ini()
124 run_core = Path(__file__).parent.parent.resolve() /\
125 "CLI" / "core" / "run_portfolio_selector_core.py"
126 cmd_list = [f"python {run_core} "
127 f"--selector {selector_path} "
128 f"--feature-data-csv {feature_dataframe.csv_filepath} "
129 f"--performance-data-csv {performance_data.csv_filepath} "
130 f"--instance {instance_path} "
131 f"--log-dir {sl.caller_log_dir}"
132 for instance_path in data_set.instance_paths]
134 selector_run = rrr.add_to_queue(
135 runner=run_on,
136 cmd=cmd_list,
137 name=CommandName.RUN_PORTFOLIO_SELECTOR,
138 base_dir=sl.caller_log_dir,
139 stdout=None,
140 dependencies=feature_run if run_on == Runner.SLURM else None,
141 sbatch_options=gv.settings().get_slurm_extra_options(as_args=True))
143 if run_on == Runner.LOCAL:
144 selector_run.wait()
145 for job in selector_run.jobs:
146 print(job.stdout)
147 print("Running Sparkle portfolio selector done!")
148 else:
149 print("Sparkle portfolio selector is running ...")
151 # Write used settings to file
152 gv.settings().write_used_settings()
153 sys.exit(0)
156if __name__ == "__main__":
157 main(sys.argv[1:])