Pick the default solver for a problem based on its structure. Checks commercial solvers first when configured to do so, then falls back to open-source defaults based on problem type. Parameters ---------- problem_form : ProblemForm Pre-canonicalization structural analys
(problem_form: ProblemForm)
| 334 | |
| 335 | |
| 336 | def pick_default_solver(problem_form: ProblemForm) -> Solver | None: |
| 337 | """Pick the default solver for a problem based on its structure. |
| 338 | |
| 339 | Checks commercial solvers first when configured to do so, then falls |
| 340 | back to open-source defaults based on problem type. |
| 341 | |
| 342 | Parameters |
| 343 | ---------- |
| 344 | problem_form : ProblemForm |
| 345 | Pre-canonicalization structural analysis of the problem. |
| 346 | |
| 347 | Returns |
| 348 | ------- |
| 349 | Solver or None |
| 350 | A solver instance, or None if no suitable installed solver is found. |
| 351 | """ |
| 352 | def _get(solver_map, name): |
| 353 | inst = solver_map.get(name) |
| 354 | return inst if inst is not None and inst.is_installed() else None |
| 355 | |
| 356 | if s.DEFAULT_TO_COMMERCIAL_SOLVERS: |
| 357 | for solver_name in slv_def.COMMERCIAL_SOLVERS: |
| 358 | solver = _get(slv_def.SOLVER_MAP_CONIC, solver_name) |
| 359 | if solver is not None and solver.can_solve(problem_form): |
| 360 | return solver |
| 361 | |
| 362 | # Mixed-integer: LP → HIGHS, non-LP → SCIP. |
| 363 | if problem_form.is_mixed_integer(): |
| 364 | if problem_form.is_lp(): |
| 365 | solver = _get(slv_def.SOLVER_MAP_CONIC, s.HIGHS) |
| 366 | if solver is not None: |
| 367 | return solver |
| 368 | solver = _get(slv_def.SOLVER_MAP_CONIC, s.SCIP) |
| 369 | if solver is not None and solver.can_solve(problem_form): |
| 370 | return solver |
| 371 | return None |
| 372 | |
| 373 | # LP → Clarabel. |
| 374 | if problem_form.cones() <= _QP_CONES: |
| 375 | return _get(slv_def.SOLVER_MAP_CONIC, s.CLARABEL) |
| 376 | |
| 377 | # QP → OSQP. |
| 378 | if problem_form.has_quadratic_objective() \ |
| 379 | and problem_form.cones(quad_obj=True) <= _QP_CONES: |
| 380 | return _get(slv_def.SOLVER_MAP_QP, s.OSQP) |
| 381 | |
| 382 | # SDP → SCS. |
| 383 | if PSD in problem_form.cones(): |
| 384 | return _get(slv_def.SOLVER_MAP_CONIC, s.SCS) |
| 385 | |
| 386 | # Everything else (SOCP, ExpCone, PowCone, etc.) → Clarabel. |
| 387 | return _get(slv_def.SOLVER_MAP_CONIC, s.CLARABEL) |
| 388 | |
| 389 | |
| 390 | def make_problem_form(problem: Problem, gp: bool, ignore_dpp: bool) -> ProblemForm: |