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

1#!/usr/bin/env python3 

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

3import sys 

4import argparse 

5from pathlib import PurePath, Path 

6 

7import runrunner as rrr 

8from runrunner import Runner 

9 

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 

20 

21 

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 

36 

37 

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

43 

44 # Define command line arguments 

45 parser = parser_function() 

46 

47 # Process command line arguments 

48 args = parser.parse_args(argv) 

49 

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) 

56 

57 # Compare current settings to latest.ini 

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

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

60 

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) 

65 

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) 

70 

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) 

84 

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) 

88 

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

96 

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] 

106 

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

118 

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

124 

125 # Write used settings to file 

126 gv.settings().write_used_settings() 

127 sys.exit(0) 

128 

129 

130if __name__ == "__main__": 

131 main(sys.argv[1:])