Coverage for sparkle/CLI/add_solver.py: 0%

82 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-27 09:10 +0000

1#!/usr/bin/env python3 

2"""Sparkle command to add a solver to the Sparkle platform.""" 

3import os 

4import stat 

5import sys 

6import argparse 

7import shutil 

8from pathlib import Path 

9 

10import runrunner as rrr 

11 

12from sparkle.platform import file_help as sfh 

13from sparkle.CLI.help import global_variables as gv 

14from sparkle.structures import PerformanceDataFrame 

15from sparkle.CLI.run_solvers import running_solvers_performance_data 

16from sparkle.solver import Solver 

17from sparkle.CLI.help import logging as sl 

18from sparkle.platform import CommandName, COMMAND_DEPENDENCIES 

19from sparkle.CLI.initialise import check_for_initialise 

20from sparkle.CLI.help import argparse_custom as ac 

21from sparkle.platform.settings_objects import SettingState 

22 

23 

24def parser_function() -> argparse.ArgumentParser: 

25 """Define the command line arguments.""" 

26 parser = argparse.ArgumentParser( 

27 description="Add a solver to the Sparkle platform.", 

28 epilog="") 

29 parser.add_argument(*ac.DeterministicArgument.names, 

30 **ac.DeterministicArgument.kwargs) 

31 parser.add_argument(*ac.RunSolverNowArgument.names, 

32 **ac.RunSolverNowArgument.kwargs) 

33 parser.add_argument(*ac.NicknameSolverArgument.names, 

34 **ac.NicknameSolverArgument.kwargs) 

35 parser.add_argument(*ac.SolverPathArgument.names, 

36 **ac.SolverPathArgument.kwargs) 

37 parser.add_argument(*ac.RunOnArgument.names, 

38 **ac.RunOnArgument.kwargs) 

39 parser.add_argument(*ac.SkipChecksArgument.names, 

40 **ac.SkipChecksArgument.kwargs) 

41 return parser 

42 

43 

44if __name__ == "__main__": 

45 # Log command call 

46 sl.log_command(sys.argv) 

47 

48 # Define command line arguments 

49 parser = parser_function() 

50 

51 # Process command line arguments 

52 args = parser.parse_args() 

53 solver_source = Path(args.solver_path) 

54 deterministic = args.deterministic 

55 

56 check_for_initialise(COMMAND_DEPENDENCIES[CommandName.ADD_SOLVER]) 

57 

58 if not solver_source.exists(): 

59 print(f'Solver path "{solver_source}" does not exist!') 

60 sys.exit(-1) 

61 

62 nickname = args.nickname 

63 

64 if args.run_on is not None: 

65 gv.settings().set_run_on( 

66 args.run_on.value, SettingState.CMD_LINE) 

67 run_on = gv.settings().get_run_on() 

68 

69 if args.run_checks: 

70 print("Running checks...") 

71 solver = Solver(Path(solver_source)) 

72 pcs_file = solver.get_pcs_file() 

73 if pcs_file is None: 

74 print("None or multiple .pcs files found. Solver " 

75 "is not valid for configuration.") 

76 else: 

77 print(f"One pcs file detected: {pcs_file.name}. ", end="") 

78 if solver.read_pcs_file(): 

79 print("Can read the pcs file.") 

80 else: 

81 print("WARNING: Can not read the provided pcs file format.") 

82 

83 configurator_wrapper_path = solver_source / Solver.wrapper 

84 if not (configurator_wrapper_path.is_file() 

85 and os.access(configurator_wrapper_path, os.X_OK)): 

86 print(f"WARNING: Solver {solver_source.name} does not have a solver wrapper " 

87 f"(Missing file {Solver.wrapper}) or is not executable. ") 

88 

89 # Start add solver 

90 solver_directory = gv.settings().DEFAULT_solver_dir / solver_source.name 

91 if not solver_directory.exists(): 

92 solver_directory.mkdir(parents=True, exist_ok=True) 

93 else: 

94 print(f"ERROR: Solver {solver_source.name} already exists! " 

95 "Can not add new solver.") 

96 sys.exit(-1) 

97 shutil.copytree(solver_source, solver_directory, dirs_exist_ok=True) 

98 # Save the deterministic bool in the solver 

99 with (solver_directory / Solver.meta_data).open("w+") as fout: 

100 fout.write(str({"deterministic": deterministic})) 

101 

102 # Add RunSolver executable to the solver 

103 runsolver_path = gv.settings().DEFAULT_runsolver_exec 

104 if runsolver_path.name in [file.name for file in solver_directory.iterdir()]: 

105 print("Warning! RunSolver executable detected in Solver " 

106 f"{solver_source.name}. This will be replaced with " 

107 f"Sparkle's version of RunSolver. ({runsolver_path})") 

108 runsolver_target = solver_directory / runsolver_path.name 

109 shutil.copyfile(runsolver_path, runsolver_target) 

110 runsolver_target.chmod(os.stat(runsolver_target).st_mode | stat.S_IEXEC) 

111 

112 performance_data = PerformanceDataFrame( 

113 gv.settings().DEFAULT_performance_data_path, 

114 objectives=gv.settings().get_general_sparkle_objectives()) 

115 performance_data.add_solver(solver_directory) 

116 performance_data.save_csv() 

117 

118 print(f"Adding solver {solver_source.name} done!") 

119 

120 if nickname is not None: 

121 sfh.add_remove_platform_item(solver_directory, 

122 gv.solver_nickname_list_path, key=nickname) 

123 

124 if args.run_solver_now: 

125 num_job_in_parallel = gv.settings().get_number_of_jobs_in_parallel() 

126 dependency_run_list = [running_solvers_performance_data( 

127 gv.settings().DEFAULT_performance_data_path, num_job_in_parallel, 

128 rerun=False, run_on=run_on 

129 )] 

130 

131 sbatch_options = gv.settings().get_slurm_extra_options(as_args=True) 

132 srun_options = ["-N1", "-n1"] + sbatch_options 

133 run_construct_portfolio_selector = rrr.add_to_queue( 

134 cmd="sparkle/CLI/construct_portfolio_selector.py", 

135 name=CommandName.CONSTRUCT_PORTFOLIO_SELECTOR, 

136 dependencies=dependency_run_list, 

137 base_dir=sl.caller_log_dir, 

138 sbatch_options=sbatch_options, 

139 srun_options=srun_options) 

140 

141 dependency_run_list.append(run_construct_portfolio_selector) 

142 

143 run_generate_report = rrr.add_to_queue( 

144 cmd="sparkle/CLI/generate_report.py", 

145 name=CommandName.GENERATE_REPORT, 

146 dependencies=dependency_run_list, 

147 base_dir=sl.caller_log_dir, 

148 sbatch_options=sbatch_options, 

149 srun_options=srun_options) 

150 

151 # Write used settings to file 

152 gv.settings().write_used_settings()