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

1#!/usr/bin/env python3 

2"""Sparkle command to execute a portfolio selector.""" 

3 

4import sys 

5import argparse 

6from pathlib import PurePath, Path 

7 

8import runrunner as rrr 

9from runrunner import Runner 

10 

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 

22 

23 

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) 

37 

38 return parser 

39 

40 

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) 

45 

46 # Define command line arguments 

47 parser = parser_function() 

48 

49 # Process command line arguments 

50 args = parser.parse_args(argv) 

51 

52 check_for_initialise(COMMAND_DEPENDENCIES[CommandName.RUN_PORTFOLIO_SELECTOR]) 

53 

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) 

63 

64 # Compare current settings to latest.ini 

65 prev_settings = Settings(PurePath("Settings/latest.ini")) 

66 Settings.check_settings_changes(gv.settings(), prev_settings) 

67 

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) 

72 

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) 

77 

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) 

85 

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) 

95 

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) 

105 

106 if run_on == Runner.LOCAL: 

107 feature_run.wait() 

108 

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

123 

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] 

133 

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

142 

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

150 

151 # Write used settings to file 

152 gv.settings().write_used_settings() 

153 sys.exit(0) 

154 

155 

156if __name__ == "__main__": 

157 main(sys.argv[1:])