Coverage for sparkle/CLI/run_portfolio_selector.py: 95%
66 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 13:21 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 13:21 +0000
1#!/usr/bin/env python3
2"""Sparkle command to execute a portfolio selector."""
3import sys
4import argparse
5from pathlib import PurePath, Path
7import runrunner as rrr
8from runrunner import Runner
10from sparkle.CLI.help import global_variables as gv
11from sparkle.CLI.help import logging as sl
12from sparkle.platform.settings_objects import Settings, SettingState
13from sparkle.CLI.help import argparse_custom as ac
14from sparkle.structures import FeatureDataFrame
15from sparkle.CLI.initialise import check_for_initialise
16from sparkle.CLI.help.nicknames import resolve_object_name
17from sparkle.instance import Instance_Set, InstanceSet
18from sparkle.CLI.compute_features import compute_features
19from sparkle.selector import SelectionScenario, Extractor
22def parser_function() -> argparse.ArgumentParser:
23 """Define the command line arguments."""
24 parser = argparse.ArgumentParser(
25 description="Run a portfolio selector on instance (set): Determine which solver "
26 "is most likely to perform well and run it on the instance (set).")
27 parser.add_argument(*ac.SelectionScenarioArgument.names,
28 **ac.SelectionScenarioArgument.kwargs)
29 parser.add_argument(*ac.InstanceSetRequiredArgument.names,
30 **ac.InstanceSetRequiredArgument.kwargs)
31 parser.add_argument(*ac.RunOnArgument.names,
32 **ac.RunOnArgument.kwargs)
33 parser.add_argument(*ac.SettingsFileArgument.names,
34 **ac.SettingsFileArgument.kwargs)
35 return parser
38def main(argv: list[str]) -> None:
39 """Main function of the run portfolio selector command."""
40 # Log command call
41 sl.log_command(sys.argv)
42 check_for_initialise()
44 # Define command line arguments
45 parser = parser_function()
47 # Process command line arguments
48 args = parser.parse_args(argv)
50 if args.settings_file is not None:
51 gv.settings().read_settings_ini(
52 args.settings_file, SettingState.CMD_LINE
53 ) # Do first, so other command line options can override settings from the file
54 if args.run_on is not None:
55 gv.settings().set_run_on(args.run_on.value, SettingState.CMD_LINE)
57 # Compare current settings to latest.ini
58 prev_settings = Settings(PurePath("Settings/latest.ini"))
59 Settings.check_settings_changes(gv.settings(), prev_settings)
61 data_set: InstanceSet = resolve_object_name(
62 args.instance,
63 gv.file_storage_data_mapping[gv.instances_nickname_path],
64 gv.settings().DEFAULT_instance_dir, Instance_Set)
66 if data_set is None:
67 print("ERROR: The instance (set) could not be found. Please make sure the "
68 "path is correct.")
69 sys.exit(-1)
71 run_on = gv.settings().get_run_on()
72 selector_scenario = SelectionScenario.from_file(args.selection_scenario)
73 # Create a new feature dataframe for this run, compute the features
74 test_case_path = selector_scenario.directory / data_set.name
75 test_case_path.mkdir(exist_ok=True)
76 feature_dataframe = FeatureDataFrame(test_case_path / "feature_data.csv")
77 feature_dataframe.remove_instances(feature_dataframe.instances)
78 for extractor_name in selector_scenario.feature_extractors:
79 extractor = resolve_object_name(
80 extractor_name,
81 gv.file_storage_data_mapping[gv.instances_nickname_path],
82 gv.settings().DEFAULT_extractor_dir, Extractor)
83 feature_dataframe.add_extractor(extractor_name, extractor.features)
85 feature_dataframe.add_instances(data_set.instances)
86 feature_dataframe.save_csv()
87 feature_run = compute_features(feature_dataframe, recompute=False, run_on=run_on)
89 if run_on == Runner.LOCAL:
90 feature_run.wait()
91 # Results need to be stored in the performance data object of the scenario:
92 # Add the instance set to it
93 for instance in data_set.instance_names:
94 selector_scenario.selector_performance_data.add_instance(str(instance))
95 selector_scenario.selector_performance_data.save_csv()
97 run_core = Path(__file__).parent.parent.resolve() /\
98 "CLI" / "core" / "run_portfolio_selector_core.py"
99 cmd_list = [
100 f"python3 {run_core} "
101 f"--selector-scenario {args.selection_scenario} "
102 f"--feature-data-csv {feature_dataframe.csv_filepath} "
103 f"--instance {instance_name} "
104 f"--log-dir {sl.caller_log_dir} "
105 for instance_name in data_set.instances]
107 import subprocess
108 selector_run = rrr.add_to_queue(
109 runner=run_on,
110 cmd=cmd_list,
111 name=f"Portfolio Selector: {selector_scenario.selector.name} on {data_set.name}",
112 stdout=None if run_on == Runner.LOCAL else subprocess.PIPE, # Print to screen
113 stderr=None if run_on == Runner.LOCAL else subprocess.PIPE, # Print to screen
114 base_dir=sl.caller_log_dir,
115 dependencies=feature_run,
116 sbatch_options=gv.settings().get_slurm_extra_options(as_args=True),
117 prepend=gv.settings().get_slurm_job_prepend())
119 if run_on == Runner.LOCAL:
120 selector_run.wait()
121 print("Running Sparkle portfolio selector done!")
122 else:
123 print("Sparkle portfolio selector is running ...")
125 # Write used settings to file
126 gv.settings().write_used_settings()
127 sys.exit(0)
130if __name__ == "__main__":
131 main(sys.argv[1:])