|
| 1 | +""" |
| 2 | +s_getInfo.py - Collect problem information from S2MPJ Python problem set |
| 3 | +
|
| 4 | +This script scans all problems in the S2MPJ Python collection and extracts |
| 5 | +various metrics including dimensions, constraint counts, and function values. |
| 6 | +The results are saved to CSV files for later use by OptiProfiler. |
| 7 | +
|
| 8 | +Usage: python s_getInfo.py |
| 9 | +""" |
| 10 | + |
| 11 | +import numpy as np |
| 12 | +import pandas as pd |
| 13 | +import signal |
| 14 | +import os |
| 15 | +import sys |
| 16 | + |
| 17 | +# Add the repository root to path so we can import s2mpj_tools |
| 18 | +cwd = os.path.dirname(os.path.abspath(__file__)) |
| 19 | +repo_root = os.path.abspath(os.path.join(cwd, '..', '..', '..')) |
| 20 | +sys.path.insert(0, repo_root) |
| 21 | + |
| 22 | +from s2mpj_tools import s2mpj_load |
| 23 | + |
| 24 | +# Set the timeout (seconds) for each problem to be loaded |
| 25 | +timeout = 50 |
| 26 | + |
| 27 | +# Read problem list from src directory |
| 28 | +filename = os.path.join(repo_root, 'src', 'list_of_python_problems') |
| 29 | +with open(filename, 'r') as file: |
| 30 | + problem_names = [line.strip().replace('.py', '') for line in file.readlines() |
| 31 | + if line.strip() and not line.startswith('#')] |
| 32 | + |
| 33 | +# Exclude problematic problems that are known to cause issues |
| 34 | +problem_exclude = [ |
| 35 | + 'SPARCO10LS', 'SPARCO10', 'SPARCO11LS', 'SPARCO11', 'SPARCO12LS', 'SPARCO12', |
| 36 | + 'SPARCO2LS', 'SPARCO2', 'SPARCO3LS', 'SPARCO3', 'SPARCO5LS', 'SPARCO5', |
| 37 | + 'SPARCO7LS', 'SPARCO7', 'SPARCO8LS', 'SPARCO8', 'SPARCO9LS', 'SPARCO9', |
| 38 | + 'ROSSIMP3_mp', 'HS67', 'HS68', 'HS69', 'HS85', 'HS88', 'HS89', 'HS90', |
| 39 | + 'HS91', 'HS92', 'TWIRIBG1' |
| 40 | +] |
| 41 | +problem_names = [name for name in problem_names if name not in problem_exclude] |
| 42 | + |
| 43 | +# List of known feasibility problems (objective function is not meaningful) |
| 44 | +known_feasibility = [ |
| 45 | + 'AIRCRFTA', 'ARGAUSS', 'ARGLALE', 'ARGLBLE', 'ARGTRIG', 'ARTIF', 'BAmL1SP', |
| 46 | + 'BARDNE', 'BEALENE', 'BENNETT5', 'BIGGS6NE', 'BOOTH', 'BOXBOD', 'BRATU2D', |
| 47 | + 'BRATU2DT', 'BRATU3D', 'BROWNBSNE', 'BROWNDENE', 'BROYDN3D', 'CBRATU2D', |
| 48 | + 'CBRATU3D', 'CHANDHEQ', 'CHEMRCTA', 'CHWIRUT2', 'CLUSTER', 'COOLHANS', |
| 49 | + 'CUBENE', 'CYCLIC3', 'CYCLOOCF', 'CYCLOOCT', 'DANIWOOD', 'DANWOOD', |
| 50 | + 'DECONVBNE', 'DENSCHNBNE', 'DENSCHNDNE', 'DENSCHNFNE', 'DEVGLA1NE', |
| 51 | + 'DEVGLA2NE', 'DRCAVTY1', 'DRCAVTY2', 'DRCAVTY3', 'ECKERLE4', 'EGGCRATENE', |
| 52 | + 'EIGENA', 'EIGENB', 'ELATVIDUNE', 'ENGVAL2NE', 'ENSO', 'ERRINROSNE', |
| 53 | + 'ERRINRSMNE', 'EXP2NE', 'EXTROSNBNE', 'FLOSP2HH', 'FLOSP2HL', 'FLOSP2HM', |
| 54 | + 'FLOSP2TH', 'FLOSP2TL', 'FLOSP2TM', 'FREURONE', 'GENROSEBNE', 'GOTTFR', |
| 55 | + 'GROWTH', 'GULFNE', 'HAHN1', 'HATFLDANE', 'HATFLDBNE', 'HATFLDCNE', |
| 56 | + 'HATFLDDNE', 'HATFLDENE', 'HATFLDFLNE', 'HATFLDF', 'HATFLDG', 'HELIXNE', |
| 57 | + 'HIMMELBA', 'HIMMELBC', 'HIMMELBD', 'HIMMELBFNE', 'HS1NE', 'HS25NE', |
| 58 | + 'HS2NE', 'HS8', 'HYDCAR20', 'HYDCAR6', 'HYPCIR', 'INTEGREQ', 'INTEQNE', |
| 59 | + 'KOEBHELBNE', 'KOWOSBNE', 'KSS', 'LANCZOS1', 'LANCZOS2', 'LANCZOS3', |
| 60 | + 'LEVYMONE10', 'LEVYMONE5', 'LEVYMONE6', 'LEVYMONE7', 'LEVYMONE8', |
| 61 | + 'LEVYMONE9', 'LEVYMONE', 'LIARWHDNE', 'LINVERSENE', 'LSC1', 'LSC2', |
| 62 | + 'LUKSAN11', 'LUKSAN12', 'LUKSAN13', 'LUKSAN14', 'LUKSAN17', 'LUKSAN21', |
| 63 | + 'LUKSAN22', 'MANCINONE', 'METHANB8', 'METHANL8', 'MEYER3NE', 'MGH09', |
| 64 | + 'MGH10', 'MISRA1A', 'MISRA1B', 'MISRA1C', 'MISRA1D', 'MODBEALENE', |
| 65 | + 'MSQRTA', 'MSQRTB', 'MUONSINE', 'n10FOLDTR', 'NELSON', 'NONSCOMPNE', |
| 66 | + 'NYSTROM5', 'OSBORNE1', 'OSBORNE2', 'OSCIGRNE', 'OSCIPANE', 'PALMER1ANE', |
| 67 | + 'PALMER1BNE', 'PALMER1ENE', 'PALMER1NE', 'PALMER2ANE', 'PALMER2BNE', |
| 68 | + 'PALMER2ENE', 'PALMER3ANE', 'PALMER3BNE', 'PALMER3ENE', 'PALMER4ANE', |
| 69 | + 'PALMER4BNE', 'PALMER4ENE', 'PALMER5ANE', 'PALMER5BNE', 'PALMER5ENE', |
| 70 | + 'PALMER6ANE', 'PALMER6ENE', 'PALMER7ANE', 'PALMER7ENE', 'PALMER8ANE', |
| 71 | + 'PALMER8ENE', 'PENLT1NE', 'PENLT2NE', 'POROUS1', 'POROUS2', 'POWELLBS', |
| 72 | + 'POWELLSQ', 'POWERSUMNE', 'PRICE3NE', 'PRICE4NE', 'QINGNE', 'QR3D', |
| 73 | + 'RAT42', 'RAT43', 'RECIPE', 'REPEAT', 'RES', 'ROSZMAN1', 'RSNBRNE', |
| 74 | + 'SANTA', 'SEMICN2U', 'SEMICON1', 'SEMICON2', 'SPECANNE', 'SSBRYBNDNE', |
| 75 | + 'SSINE', 'THURBER', 'TQUARTICNE', 'VANDERM1', 'VANDERM2', 'VANDERM3', |
| 76 | + 'VANDERM4', 'VARDIMNE', 'VESUVIA', 'VESUVIO', 'VESUVIOU', 'VIBRBEAMNE', |
| 77 | + 'WATSONNE', 'WAYSEA1NE', 'WAYSEA2NE', 'YATP1CNE', 'YATP2CNE', 'YFITNE', |
| 78 | + 'ZANGWIL3' |
| 79 | +] |
| 80 | + |
| 81 | +# To store all the feasibility problems discovered during runtime |
| 82 | +feasibility = [] |
| 83 | + |
| 84 | +# To store all the 'time out' problems |
| 85 | +timeout_problems = [] |
| 86 | + |
| 87 | +# Output path (repository root) |
| 88 | +saving_path = repo_root |
| 89 | + |
| 90 | + |
| 91 | +class Logger: |
| 92 | + """Dual-output logger that writes to both terminal and log file.""" |
| 93 | + def __init__(self, logfile): |
| 94 | + self.terminal = sys.__stdout__ |
| 95 | + self.log = logfile |
| 96 | + |
| 97 | + def write(self, message): |
| 98 | + self.terminal.write(message) |
| 99 | + try: |
| 100 | + self.log.write(message) |
| 101 | + except Exception as e: |
| 102 | + self.terminal.write(f"[Logger Error] {e}\n") |
| 103 | + |
| 104 | + def flush(self): |
| 105 | + self.terminal.flush() |
| 106 | + self.log.flush() |
| 107 | + |
| 108 | + |
| 109 | +def run_with_timeout(func, args, timeout_seconds): |
| 110 | + """Execute a function with a timeout using SIGALRM.""" |
| 111 | + def handler(signum, frame): |
| 112 | + raise TimeoutError(f"Function timed out after {timeout_seconds} seconds") |
| 113 | + |
| 114 | + signal.signal(signal.SIGALRM, handler) |
| 115 | + signal.alarm(timeout_seconds) |
| 116 | + |
| 117 | + try: |
| 118 | + result = func(*args) if args else func() |
| 119 | + return result |
| 120 | + finally: |
| 121 | + signal.alarm(0) |
| 122 | + |
| 123 | + |
| 124 | +def get_problem_info(problem_name, known_feasibility): |
| 125 | + """ |
| 126 | + Extract information about a single problem. |
| 127 | + |
| 128 | + Returns a dictionary containing problem metrics such as dimensions, |
| 129 | + constraint counts, and function availability flags. |
| 130 | + """ |
| 131 | + print(f"Processing problem: {problem_name}") |
| 132 | + |
| 133 | + info_single = { |
| 134 | + 'problem_name': problem_name, |
| 135 | + 'ptype': 'unknown', |
| 136 | + 'xtype': 'unknown', |
| 137 | + 'dim': 'unknown', |
| 138 | + 'mb': 'unknown', |
| 139 | + 'ml': 'unknown', |
| 140 | + 'mu': 'unknown', |
| 141 | + 'mcon': 'unknown', |
| 142 | + 'mlcon': 'unknown', |
| 143 | + 'mnlcon': 'unknown', |
| 144 | + 'm_ub': 'unknown', |
| 145 | + 'm_eq': 'unknown', |
| 146 | + 'm_linear_ub': 'unknown', |
| 147 | + 'm_linear_eq': 'unknown', |
| 148 | + 'm_nonlinear_ub': 'unknown', |
| 149 | + 'm_nonlinear_eq': 'unknown', |
| 150 | + 'f0': 0, |
| 151 | + 'isfeasibility': 1, |
| 152 | + 'isgrad': 0, |
| 153 | + 'ishess': 0, |
| 154 | + 'isjcub': 0, |
| 155 | + 'isjceq': 0, |
| 156 | + 'ishcub': 0, |
| 157 | + 'ishceq': 0 |
| 158 | + } |
| 159 | + |
| 160 | + # Try to load the problem with timeout protection |
| 161 | + try: |
| 162 | + p = run_with_timeout(s2mpj_load, (problem_name,), timeout) |
| 163 | + except TimeoutError: |
| 164 | + print(f"Timeout while loading problem {problem_name}.") |
| 165 | + timeout_problems.append(problem_name) |
| 166 | + return info_single |
| 167 | + |
| 168 | + # Extract basic problem information |
| 169 | + try: |
| 170 | + info_single['ptype'] = p.ptype |
| 171 | + info_single['xtype'] = 'r' |
| 172 | + info_single['dim'] = p.n |
| 173 | + info_single['mb'] = p.mb |
| 174 | + info_single['ml'] = sum(p.xl > -np.inf) |
| 175 | + info_single['mu'] = sum(p.xu < np.inf) |
| 176 | + info_single['mcon'] = p.mcon |
| 177 | + info_single['mlcon'] = p.mlcon |
| 178 | + info_single['mnlcon'] = p.mnlcon |
| 179 | + info_single['m_ub'] = p.m_linear_ub + p.m_nonlinear_ub |
| 180 | + info_single['m_eq'] = p.m_linear_eq + p.m_nonlinear_eq |
| 181 | + info_single['m_linear_ub'] = p.m_linear_ub |
| 182 | + info_single['m_linear_eq'] = p.m_linear_eq |
| 183 | + info_single['m_nonlinear_ub'] = p.m_nonlinear_ub |
| 184 | + info_single['m_nonlinear_eq'] = p.m_nonlinear_eq |
| 185 | + except Exception as e: |
| 186 | + print(f"Error while getting problem info for {problem_name}: {e}") |
| 187 | + |
| 188 | + # Evaluate the objective function to determine if it's a feasibility problem |
| 189 | + try: |
| 190 | + f = run_with_timeout(p.fun, (p.x0,), timeout) |
| 191 | + if problem_name == 'LIN': |
| 192 | + info_single['isfeasibility'] = 0 |
| 193 | + info_single['f0'] = np.nan |
| 194 | + elif np.size(f) == 0 or np.isnan(f) or problem_name in known_feasibility: |
| 195 | + info_single['isfeasibility'] = 1 |
| 196 | + info_single['f0'] = 0 |
| 197 | + feasibility.append(problem_name) |
| 198 | + else: |
| 199 | + info_single['isfeasibility'] = 0 |
| 200 | + info_single['f0'] = f |
| 201 | + except Exception as e: |
| 202 | + print(f"Error while evaluating function for {problem_name}: {e}") |
| 203 | + info_single['f0'] = 0 |
| 204 | + info_single['isfeasibility'] = 1 |
| 205 | + feasibility.append(problem_name) |
| 206 | + |
| 207 | + # Check availability of gradient, Hessian, and constraint Jacobians/Hessians |
| 208 | + if problem_name in feasibility: |
| 209 | + info_single['isgrad'] = 1 |
| 210 | + info_single['ishess'] = 1 |
| 211 | + else: |
| 212 | + try: |
| 213 | + g = run_with_timeout(p.grad, (p.x0,), timeout) |
| 214 | + info_single['isgrad'] = 1 if g.size > 0 else 0 |
| 215 | + except Exception: |
| 216 | + info_single['isgrad'] = 0 |
| 217 | + |
| 218 | + try: |
| 219 | + h = run_with_timeout(p.hess, (p.x0,), timeout) |
| 220 | + info_single['ishess'] = 1 if h.size > 0 else 0 |
| 221 | + except Exception: |
| 222 | + info_single['ishess'] = 0 |
| 223 | + |
| 224 | + try: |
| 225 | + jc = run_with_timeout(p.jcub, (p.x0,), timeout) |
| 226 | + info_single['isjcub'] = 1 if jc.size > 0 else 0 |
| 227 | + except Exception: |
| 228 | + info_single['isjcub'] = 0 |
| 229 | + |
| 230 | + try: |
| 231 | + jc = run_with_timeout(p.jceq, (p.x0,), timeout) |
| 232 | + info_single['isjceq'] = 1 if jc.size > 0 else 0 |
| 233 | + except Exception: |
| 234 | + info_single['isjceq'] = 0 |
| 235 | + |
| 236 | + try: |
| 237 | + hc = run_with_timeout(p.hcub, (p.x0,), timeout) |
| 238 | + info_single['ishcub'] = 1 if len(hc) > 0 else 0 |
| 239 | + except Exception: |
| 240 | + info_single['ishcub'] = 0 |
| 241 | + |
| 242 | + try: |
| 243 | + hc = run_with_timeout(p.hceq, (p.x0,), timeout) |
| 244 | + info_single['ishceq'] = 1 if len(hc) > 0 else 0 |
| 245 | + except Exception: |
| 246 | + info_single['ishceq'] = 0 |
| 247 | + |
| 248 | + print(f"Finished processing problem {problem_name}.") |
| 249 | + return info_single |
| 250 | + |
| 251 | + |
| 252 | +if __name__ == "__main__": |
| 253 | + # Set up logging to both terminal and file |
| 254 | + log_file = open(os.path.join(saving_path, 'log_python.txt'), 'w') |
| 255 | + sys.stdout = Logger(log_file) |
| 256 | + sys.stderr = Logger(log_file) |
| 257 | + |
| 258 | + # Process all problems and collect their information |
| 259 | + results = [] |
| 260 | + for name in problem_names: |
| 261 | + info = get_problem_info(name, known_feasibility) |
| 262 | + results.append(info) |
| 263 | + sys.stdout.flush() |
| 264 | + sys.stderr.flush() |
| 265 | + |
| 266 | + # Create DataFrame and filter out problems with 'unknown' values |
| 267 | + df = pd.DataFrame(results) |
| 268 | + |
| 269 | + def has_unknown_values(row): |
| 270 | + for value in row: |
| 271 | + if str(value).strip().lower() == 'unknown': |
| 272 | + return True |
| 273 | + return False |
| 274 | + |
| 275 | + unknown_mask = df.apply(has_unknown_values, axis=1) |
| 276 | + if unknown_mask.any(): |
| 277 | + filtered_problems = df.loc[unknown_mask, 'problem_name'].tolist() |
| 278 | + print(f"Filtered out {len(filtered_problems)} problems with 'unknown' values:") |
| 279 | + for problem in filtered_problems: |
| 280 | + print(f" - {problem}") |
| 281 | + |
| 282 | + df_clean = df[~unknown_mask] |
| 283 | + |
| 284 | + # Save results to CSV |
| 285 | + df_clean.to_csv(os.path.join(saving_path, 'probinfo_python.csv'), index=False, na_rep='nan') |
| 286 | + |
| 287 | + # Save feasibility problems list |
| 288 | + with open(os.path.join(saving_path, 'feasibility_python.txt'), 'w') as f: |
| 289 | + f.write(' '.join(feasibility)) |
| 290 | + |
| 291 | + # Save timeout problems list |
| 292 | + with open(os.path.join(saving_path, 'timeout_problems_python.txt'), 'w') as f: |
| 293 | + f.write(' '.join(timeout_problems)) |
| 294 | + |
| 295 | + print("Script completed successfully.") |
| 296 | + |
| 297 | + # Clean up logging |
| 298 | + log_file.close() |
| 299 | + sys.stdout = sys.__stdout__ |
| 300 | + sys.stderr = sys.__stderr__ |
0 commit comments