diff --git a/cpp/include/cuopt/linear_programming/solver_settings.hpp b/cpp/include/cuopt/linear_programming/solver_settings.hpp index dd910a8f4..61e84c6cd 100644 --- a/cpp/include/cuopt/linear_programming/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/solver_settings.hpp @@ -94,6 +94,7 @@ class solver_settings_t { const std::vector>& get_int_parameters() const; const std::vector>& get_bool_parameters() const; const std::vector>& get_string_parameters() const; + const std::vector get_parameter_names() const; private: pdlp_solver_settings_t pdlp_settings; diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index 493e730fb..5f029db61 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -440,6 +440,25 @@ solver_settings_t::get_string_parameters() const return string_parameters; } +template +const std::vector solver_settings_t::get_parameter_names() const +{ + std::vector parameter_names; + for (auto& param : int_parameters) { + parameter_names.push_back(param.param_name); + } + for (auto& param : float_parameters) { + parameter_names.push_back(param.param_name); + } + for (auto& param : bool_parameters) { + parameter_names.push_back(param.param_name); + } + for (auto& param : string_parameters) { + parameter_names.push_back(param.param_name); + } + return parameter_names; +} + #if MIP_INSTANTIATE_FLOAT template class solver_settings_t; template void solver_settings_t::set_parameter(const std::string& name, int value); diff --git a/python/cuopt/cuopt/linear_programming/solver/solver.pxd b/python/cuopt/cuopt/linear_programming/solver/solver.pxd index 6688e5166..83b1e0054 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver.pxd +++ b/python/cuopt/cuopt/linear_programming/solver/solver.pxd @@ -62,6 +62,8 @@ cdef extern from "cuopt/linear_programming/solver_settings.hpp" namespace "cuopt string get_parameter_as_string(const string& name) except + + vector[string] get_parameter_names() except + + # LP settings void set_initial_pdlp_primal_solution( const f_t* initial_primal_solution, diff --git a/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx b/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx index a336e269f..871919dad 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx +++ b/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # noqa +# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # noqa # SPDX-License-Identifier: Apache-2.0 @@ -9,10 +9,10 @@ from libcpp.memory cimport unique_ptr from libcpp.string cimport string +from libcpp.vector cimport vector from cuopt.linear_programming.solver.solver cimport solver_settings_t - def get_solver_setting(name): cdef unique_ptr[solver_settings_t[int, double]] unique_solver_settings @@ -25,87 +25,21 @@ def get_solver_setting(name): name.encode('utf-8') ).decode('utf-8') -# Get the C #defines from the constants.h file -cdef extern from "cuopt/linear_programming/constants.h": # noqa - cdef const char* c_CUOPT_ABSOLUTE_DUAL_TOLERANCE "CUOPT_ABSOLUTE_DUAL_TOLERANCE" # noqa - cdef const char* c_CUOPT_RELATIVE_DUAL_TOLERANCE "CUOPT_RELATIVE_DUAL_TOLERANCE" # noqa - cdef const char* c_CUOPT_ABSOLUTE_PRIMAL_TOLERANCE "CUOPT_ABSOLUTE_PRIMAL_TOLERANCE" # noqa - cdef const char* c_CUOPT_RELATIVE_PRIMAL_TOLERANCE "CUOPT_RELATIVE_PRIMAL_TOLERANCE" # noqa - cdef const char* c_CUOPT_ABSOLUTE_GAP_TOLERANCE "CUOPT_ABSOLUTE_GAP_TOLERANCE" # noqa - cdef const char* c_CUOPT_RELATIVE_GAP_TOLERANCE "CUOPT_RELATIVE_GAP_TOLERANCE" # noqa - cdef const char* c_CUOPT_INFEASIBILITY_DETECTION "CUOPT_INFEASIBILITY_DETECTION" # noqa - cdef const char* c_CUOPT_STRICT_INFEASIBILITY "CUOPT_STRICT_INFEASIBILITY" # noqa - cdef const char* c_CUOPT_PRIMAL_INFEASIBLE_TOLERANCE "CUOPT_PRIMAL_INFEASIBLE_TOLERANCE" # noqa - cdef const char* c_CUOPT_DUAL_INFEASIBLE_TOLERANCE "CUOPT_DUAL_INFEASIBLE_TOLERANCE" # noqa - cdef const char* c_CUOPT_ITERATION_LIMIT "CUOPT_ITERATION_LIMIT" # noqa - cdef const char* c_CUOPT_TIME_LIMIT "CUOPT_TIME_LIMIT" # noqa - cdef const char* c_CUOPT_PDLP_SOLVER_MODE "CUOPT_PDLP_SOLVER_MODE" # noqa - cdef const char* c_CUOPT_METHOD "CUOPT_METHOD" # noqa - cdef const char* c_CUOPT_PER_CONSTRAINT_RESIDUAL "CUOPT_PER_CONSTRAINT_RESIDUAL" # noqa - cdef const char* c_CUOPT_SAVE_BEST_PRIMAL_SO_FAR "CUOPT_SAVE_BEST_PRIMAL_SO_FAR" # noqa - cdef const char* c_CUOPT_FIRST_PRIMAL_FEASIBLE "CUOPT_FIRST_PRIMAL_FEASIBLE" # noqa - cdef const char* c_CUOPT_LOG_FILE "CUOPT_LOG_FILE" # noqa - cdef const char* c_CUOPT_LOG_TO_CONSOLE "CUOPT_LOG_TO_CONSOLE" # noqa - cdef const char* c_CUOPT_CROSSOVER "CUOPT_CROSSOVER" # noqa - cdef const char* c_CUOPT_PRESOLVE "CUOPT_PRESOLVE" # noqa - cdef const char* c_CUOPT_DUAL_POSTSOLVE "CUOPT_DUAL_POSTSOLVE" # noqa - cdef const char* c_CUOPT_NUM_GPUS "CUOPT_NUM_GPUS" # noqa - cdef const char* c_CUOPT_MIP_ABSOLUTE_TOLERANCE "CUOPT_MIP_ABSOLUTE_TOLERANCE" # noqa - cdef const char* c_CUOPT_MIP_RELATIVE_TOLERANCE "CUOPT_MIP_RELATIVE_TOLERANCE" # noqa - cdef const char* c_CUOPT_MIP_INTEGRALITY_TOLERANCE "CUOPT_MIP_INTEGRALITY_TOLERANCE" # noqa - cdef const char* c_CUOPT_MIP_ABSOLUTE_GAP "CUOPT_MIP_ABSOLUTE_GAP" # noqa - cdef const char* c_CUOPT_MIP_RELATIVE_GAP "CUOPT_MIP_RELATIVE_GAP" # noqa - cdef const char* c_CUOPT_MIP_HEURISTICS_ONLY "CUOPT_MIP_HEURISTICS_ONLY" # noqa - cdef const char* c_CUOPT_MIP_SCALING "CUOPT_MIP_SCALING" # noqa - cdef const char* c_CUOPT_SOLUTION_FILE "CUOPT_SOLUTION_FILE" # noqa - cdef const char* c_CUOPT_NUM_CPU_THREADS "CUOPT_NUM_CPU_THREADS" # noqa - cdef const char* c_CUOPT_USER_PROBLEM_FILE "CUOPT_USER_PROBLEM_FILE" # noqa - cdef const char* c_CUOPT_AUGMENTED "CUOPT_AUGMENTED" - cdef const char* c_CUOPT_FOLDING "CUOPT_FOLDING" - cdef const char* c_CUOPT_DUALIZE "CUOPT_DUALIZE" - cdef const char* c_CUOPT_ELIMINATE_DENSE_COLUMNS "CUOPT_ELIMINATE_DENSE_COLUMNS" # noqa - cdef const char* c_CUOPT_CUDSS_DETERMINISTIC "CUOPT_CUDSS_DETERMINISTIC" # noqa - cdef const char* c_CUOPT_ORDERING "CUOPT_ORDERING" # noqa - cdef const char* c_CUOPT_BARRIER_DUAL_INITIAL_POINT "CUOPT_BARRIER_DUAL_INITIAL_POINT" # noqa -# Create Python string constants from C string literals -CUOPT_ABSOLUTE_DUAL_TOLERANCE = c_CUOPT_ABSOLUTE_DUAL_TOLERANCE.decode('utf-8') # noqa -CUOPT_RELATIVE_DUAL_TOLERANCE = c_CUOPT_RELATIVE_DUAL_TOLERANCE.decode('utf-8') # noqa -CUOPT_ABSOLUTE_PRIMAL_TOLERANCE = c_CUOPT_ABSOLUTE_PRIMAL_TOLERANCE.decode('utf-8') # noqa -CUOPT_RELATIVE_PRIMAL_TOLERANCE = c_CUOPT_RELATIVE_PRIMAL_TOLERANCE.decode('utf-8') # noqa -CUOPT_ABSOLUTE_GAP_TOLERANCE = c_CUOPT_ABSOLUTE_GAP_TOLERANCE.decode('utf-8') # noqa -CUOPT_RELATIVE_GAP_TOLERANCE = c_CUOPT_RELATIVE_GAP_TOLERANCE.decode('utf-8') # noqa -CUOPT_INFEASIBILITY_DETECTION = c_CUOPT_INFEASIBILITY_DETECTION.decode('utf-8') # noqa -CUOPT_STRICT_INFEASIBILITY = c_CUOPT_STRICT_INFEASIBILITY.decode('utf-8') # noqa -CUOPT_PRIMAL_INFEASIBLE_TOLERANCE = c_CUOPT_PRIMAL_INFEASIBLE_TOLERANCE.decode('utf-8') # noqa -CUOPT_DUAL_INFEASIBLE_TOLERANCE = c_CUOPT_DUAL_INFEASIBLE_TOLERANCE.decode('utf-8') # noqa -CUOPT_ITERATION_LIMIT = c_CUOPT_ITERATION_LIMIT.decode('utf-8') # noqa -CUOPT_TIME_LIMIT = c_CUOPT_TIME_LIMIT.decode('utf-8') # noqa -CUOPT_PDLP_SOLVER_MODE = c_CUOPT_PDLP_SOLVER_MODE.decode('utf-8') # noqa -CUOPT_METHOD = c_CUOPT_METHOD.decode('utf-8') # noqa -CUOPT_PER_CONSTRAINT_RESIDUAL = c_CUOPT_PER_CONSTRAINT_RESIDUAL.decode('utf-8') # noqa -CUOPT_SAVE_BEST_PRIMAL_SO_FAR = c_CUOPT_SAVE_BEST_PRIMAL_SO_FAR.decode('utf-8') # noqa -CUOPT_FIRST_PRIMAL_FEASIBLE = c_CUOPT_FIRST_PRIMAL_FEASIBLE.decode('utf-8') # noqa -CUOPT_LOG_FILE = c_CUOPT_LOG_FILE.decode('utf-8') # noqa -CUOPT_LOG_TO_CONSOLE = c_CUOPT_LOG_TO_CONSOLE.decode('utf-8') # noqa -CUOPT_CROSSOVER = c_CUOPT_CROSSOVER.decode('utf-8') # noqa -CUOPT_PRESOLVE = c_CUOPT_PRESOLVE.decode('utf-8') # noqa -CUOPT_DUAL_POSTSOLVE = c_CUOPT_DUAL_POSTSOLVE.decode('utf-8') # noqa -CUOPT_NUM_GPUS = c_CUOPT_NUM_GPUS.decode('utf-8') # noqa -CUOPT_MIP_ABSOLUTE_TOLERANCE = c_CUOPT_MIP_ABSOLUTE_TOLERANCE.decode('utf-8') # noqa -CUOPT_MIP_RELATIVE_TOLERANCE = c_CUOPT_MIP_RELATIVE_TOLERANCE.decode('utf-8') # noqa -CUOPT_MIP_INTEGRALITY_TOLERANCE = c_CUOPT_MIP_INTEGRALITY_TOLERANCE.decode('utf-8') # noqa -CUOPT_MIP_ABSOLUTE_GAP = c_CUOPT_MIP_ABSOLUTE_GAP.decode('utf-8') # noqa -CUOPT_MIP_RELATIVE_GAP = c_CUOPT_MIP_RELATIVE_GAP.decode('utf-8') # noqa -CUOPT_MIP_HEURISTICS_ONLY = c_CUOPT_MIP_HEURISTICS_ONLY.decode('utf-8') # noqa -CUOPT_MIP_SCALING = c_CUOPT_MIP_SCALING.decode('utf-8') # noqa -CUOPT_SOLUTION_FILE = c_CUOPT_SOLUTION_FILE.decode('utf-8') # noqa -CUOPT_NUM_CPU_THREADS = c_CUOPT_NUM_CPU_THREADS.decode('utf-8') # noqa -CUOPT_USER_PROBLEM_FILE = c_CUOPT_USER_PROBLEM_FILE.decode('utf-8') # noqa -CUOPT_AUGMENTED = c_CUOPT_AUGMENTED.decode('utf-8') # noqa -CUOPT_FOLDING = c_CUOPT_FOLDING.decode('utf-8') # noqa -CUOPT_DUALIZE = c_CUOPT_DUALIZE.decode('utf-8') # noqa -CUOPT_ELIMINATE_DENSE_COLUMNS = c_CUOPT_ELIMINATE_DENSE_COLUMNS.decode('utf-8') # noqa -CUOPT_CUDSS_DETERMINISTIC = c_CUOPT_CUDSS_DETERMINISTIC.decode('utf-8') # noqa -CUOPT_ORDERING = c_CUOPT_ORDERING.decode('utf-8') # noqa -CUOPT_BARRIER_DUAL_INITIAL_POINT = c_CUOPT_BARRIER_DUAL_INITIAL_POINT.decode('utf-8') # noqa +cpdef get_solver_parameter_names(): + cdef unique_ptr[solver_settings_t[int, double]] unique_solver_settings + unique_solver_settings.reset(new solver_settings_t[int, double]()) + cdef solver_settings_t[int, double]* c_solver_settings = ( + unique_solver_settings.get() + ) + cdef vector[string] parameter_names = c_solver_settings.get_parameter_names() + + cdef list py_parameter_names = [] + cdef size_t i + for i in range(parameter_names.size()): + # std::string -> Python str + py_parameter_names.append(parameter_names[i].decode("utf-8")) + return py_parameter_names + +solver_params = get_solver_parameter_names() +for param in solver_params: globals()["CUOPT_"+param.upper()] = param diff --git a/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py b/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py index cbefbfcdd..646e4c65a 100644 --- a/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py +++ b/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py @@ -4,46 +4,7 @@ from enum import IntEnum, auto from cuopt.linear_programming.solver.solver_parameters import ( - CUOPT_ABSOLUTE_DUAL_TOLERANCE, - CUOPT_ABSOLUTE_GAP_TOLERANCE, - CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, - CUOPT_AUGMENTED, - CUOPT_BARRIER_DUAL_INITIAL_POINT, - CUOPT_CROSSOVER, - CUOPT_CUDSS_DETERMINISTIC, - CUOPT_DUAL_INFEASIBLE_TOLERANCE, - CUOPT_DUAL_POSTSOLVE, - CUOPT_DUALIZE, - CUOPT_ELIMINATE_DENSE_COLUMNS, - CUOPT_FIRST_PRIMAL_FEASIBLE, - CUOPT_FOLDING, - CUOPT_INFEASIBILITY_DETECTION, - CUOPT_ITERATION_LIMIT, - CUOPT_LOG_FILE, - CUOPT_LOG_TO_CONSOLE, - CUOPT_METHOD, - CUOPT_MIP_ABSOLUTE_GAP, - CUOPT_MIP_ABSOLUTE_TOLERANCE, - CUOPT_MIP_HEURISTICS_ONLY, - CUOPT_MIP_INTEGRALITY_TOLERANCE, - CUOPT_MIP_RELATIVE_GAP, - CUOPT_MIP_RELATIVE_TOLERANCE, - CUOPT_MIP_SCALING, - CUOPT_NUM_CPU_THREADS, - CUOPT_NUM_GPUS, - CUOPT_ORDERING, - CUOPT_PDLP_SOLVER_MODE, - CUOPT_PER_CONSTRAINT_RESIDUAL, - CUOPT_PRESOLVE, - CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, - CUOPT_RELATIVE_DUAL_TOLERANCE, - CUOPT_RELATIVE_GAP_TOLERANCE, - CUOPT_RELATIVE_PRIMAL_TOLERANCE, - CUOPT_SAVE_BEST_PRIMAL_SO_FAR, - CUOPT_SOLUTION_FILE, - CUOPT_STRICT_INFEASIBILITY, - CUOPT_TIME_LIMIT, - CUOPT_USER_PROBLEM_FILE, + solver_params, get_solver_setting, ) @@ -156,6 +117,8 @@ def get_parameter(self, name): For a list of availabe parameters, their descriptions, default values, and acceptable ranges, see the cuOpt documentation `parameter.rst`. """ + if name not in solver_params: + raise ValueError("Invalid parameter. Please check documentation") if name in self.settings_dict: if isinstance(self.settings_dict[name], str): return self.to_base_type(self.settings_dict[name]) @@ -179,7 +142,8 @@ def set_parameter(self, name, value): For a list of availabe parameters, their descriptions, default values, and acceptable ranges, see the cuOpt documentation `parameter.rst`. """ - + if name not in solver_params: + raise ValueError("Invalid parameter. Please check documentation") self.settings_dict[name] = value def set_optimality_tolerance(self, eps_optimal): @@ -211,12 +175,9 @@ def set_optimality_tolerance(self, eps_optimal): Default value is 1e-4. To set each absolute and relative tolerance, use the provided setters. """ - self.settings_dict["absolute_dual_tolerance"] = eps_optimal - self.settings_dict["relative_dual_tolerance"] = eps_optimal - self.settings_dict["absolute_primal_tolerance"] = eps_optimal - self.settings_dict["relative_primal_tolerance"] = eps_optimal - self.settings_dict["absolute_gap_tolerance"] = eps_optimal - self.settings_dict["relative_gap_tolerance"] = eps_optimal + for param in solver_params: + if param.endswith("tolerance") and not param.startswith("mip"): + self.settings_dict[param] = eps_optimal def set_pdlp_warm_start_data(self, pdlp_warm_start_data): """ @@ -339,93 +300,15 @@ def get_pdlp_warm_start_data(self): return self.pdlp_warm_start_data def toDict(self): - time_limit = self.get_parameter(CUOPT_TIME_LIMIT) - if time_limit == float("inf"): - time_limit = None - - solver_config = { - "tolerances": { - "absolute_dual_tolerance": self.get_parameter( - CUOPT_ABSOLUTE_DUAL_TOLERANCE - ), - "relative_dual_tolerance": self.get_parameter( - CUOPT_RELATIVE_DUAL_TOLERANCE - ), - "absolute_primal_tolerance": self.get_parameter( - CUOPT_ABSOLUTE_PRIMAL_TOLERANCE - ), - "relative_primal_tolerance": self.get_parameter( - CUOPT_RELATIVE_PRIMAL_TOLERANCE - ), - "absolute_gap_tolerance": self.get_parameter( - CUOPT_ABSOLUTE_GAP_TOLERANCE - ), - "relative_gap_tolerance": self.get_parameter( - CUOPT_RELATIVE_GAP_TOLERANCE - ), - "primal_infeasible_tolerance": self.get_parameter( - CUOPT_PRIMAL_INFEASIBLE_TOLERANCE - ), - "dual_infeasible_tolerance": self.get_parameter( - CUOPT_DUAL_INFEASIBLE_TOLERANCE - ), - "mip_integrality_tolerance": self.get_parameter( - CUOPT_MIP_INTEGRALITY_TOLERANCE - ), - "mip_absolute_gap": self.get_parameter(CUOPT_MIP_ABSOLUTE_GAP), - "mip_relative_gap": self.get_parameter(CUOPT_MIP_RELATIVE_GAP), - "mip_absolute_tolerance": self.get_parameter( - CUOPT_MIP_ABSOLUTE_TOLERANCE - ), - "mip_relative_tolerance": self.get_parameter( - CUOPT_MIP_RELATIVE_TOLERANCE - ), - }, - "infeasibility_detection": self.get_parameter( - CUOPT_INFEASIBILITY_DETECTION - ), - "time_limit": time_limit, - "iteration_limit": self.get_parameter(CUOPT_ITERATION_LIMIT), - "pdlp_solver_mode": self.get_parameter(CUOPT_PDLP_SOLVER_MODE), - "method": self.get_parameter(CUOPT_METHOD), - "presolve": self.get_parameter(CUOPT_PRESOLVE), - "dual_postsolve": self.get_parameter(CUOPT_DUAL_POSTSOLVE), - "mip_scaling": self.get_parameter(CUOPT_MIP_SCALING), - "mip_heuristics_only": self.get_parameter( - CUOPT_MIP_HEURISTICS_ONLY - ), - "num_cpu_threads": self.get_parameter(CUOPT_NUM_CPU_THREADS), - "num_gpus": self.get_parameter(CUOPT_NUM_GPUS), - "augmented": self.get_parameter(CUOPT_AUGMENTED), - "folding": self.get_parameter(CUOPT_FOLDING), - "dualize": self.get_parameter(CUOPT_DUALIZE), - "ordering": self.get_parameter(CUOPT_ORDERING), - "barrier_dual_initial_point": self.get_parameter( - CUOPT_BARRIER_DUAL_INITIAL_POINT - ), - "eliminate_dense_columns": self.get_parameter( - CUOPT_ELIMINATE_DENSE_COLUMNS - ), - "cudss_deterministic": self.get_parameter( - CUOPT_CUDSS_DETERMINISTIC - ), - "crossover": self.get_parameter(CUOPT_CROSSOVER), - "log_to_console": self.get_parameter(CUOPT_LOG_TO_CONSOLE), - "first_primal_feasible": self.get_parameter( - CUOPT_FIRST_PRIMAL_FEASIBLE - ), - "log_file": self.get_parameter(CUOPT_LOG_FILE), - "per_constraint_residual": self.get_parameter( - CUOPT_PER_CONSTRAINT_RESIDUAL - ), - "save_best_primal_so_far": self.get_parameter( - CUOPT_SAVE_BEST_PRIMAL_SO_FAR - ), - "solution_file": self.get_parameter(CUOPT_SOLUTION_FILE), - "strict_infeasibility": self.get_parameter( - CUOPT_STRICT_INFEASIBILITY - ), - "user_problem_file": self.get_parameter(CUOPT_USER_PROBLEM_FILE), - } + solver_config = {} + solver_config["tolerances"] = {} + for param in solver_params: + if param.endswith("tolerance"): + solver_config["tolerances"][param] = self.get_parameter(param) + else: + param_value = self.get_parameter(param) + if param_value == float("inf"): + param_value = None + solver_config[param] = param_value return solver_config diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py index 5b49e25d7..1ef8340e2 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py @@ -12,46 +12,7 @@ GetSolutionCallback, SetSolutionCallback, ) -from cuopt.linear_programming.solver.solver_parameters import ( - CUOPT_ABSOLUTE_DUAL_TOLERANCE, - CUOPT_ABSOLUTE_GAP_TOLERANCE, - CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, - CUOPT_AUGMENTED, - CUOPT_BARRIER_DUAL_INITIAL_POINT, - CUOPT_CROSSOVER, - CUOPT_CUDSS_DETERMINISTIC, - CUOPT_DUAL_INFEASIBLE_TOLERANCE, - CUOPT_DUAL_POSTSOLVE, - CUOPT_DUALIZE, - CUOPT_ELIMINATE_DENSE_COLUMNS, - CUOPT_FIRST_PRIMAL_FEASIBLE, - CUOPT_FOLDING, - CUOPT_INFEASIBILITY_DETECTION, - CUOPT_ITERATION_LIMIT, - CUOPT_LOG_FILE, - CUOPT_LOG_TO_CONSOLE, - CUOPT_METHOD, - CUOPT_MIP_ABSOLUTE_GAP, - CUOPT_MIP_ABSOLUTE_TOLERANCE, - CUOPT_MIP_HEURISTICS_ONLY, - CUOPT_MIP_INTEGRALITY_TOLERANCE, - CUOPT_MIP_RELATIVE_GAP, - CUOPT_MIP_RELATIVE_TOLERANCE, - CUOPT_MIP_SCALING, - CUOPT_NUM_CPU_THREADS, - CUOPT_NUM_GPUS, - CUOPT_ORDERING, - CUOPT_PDLP_SOLVER_MODE, - CUOPT_PER_CONSTRAINT_RESIDUAL, - CUOPT_PRESOLVE, - CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, - CUOPT_RELATIVE_DUAL_TOLERANCE, - CUOPT_RELATIVE_GAP_TOLERANCE, - CUOPT_RELATIVE_PRIMAL_TOLERANCE, - CUOPT_SAVE_BEST_PRIMAL_SO_FAR, - CUOPT_STRICT_INFEASIBILITY, - CUOPT_TIME_LIMIT, -) +from cuopt.linear_programming.solver.solver_parameters import solver_params from cuopt.linear_programming.solver.solver_wrapper import ( ErrorStatus, LPTerminationStatus, @@ -195,29 +156,18 @@ def create_solver(LP_data, warmstart_data): if LP_data.solver_config is not None: solver_config = LP_data.solver_config - if solver_config.infeasibility_detection is not None: - solver_settings.set_parameter( - CUOPT_INFEASIBILITY_DETECTION, - solver_config.infeasibility_detection, - ) - if solver_config.pdlp_solver_mode is not None: - solver_settings.set_parameter( - CUOPT_PDLP_SOLVER_MODE, - linear_programming.solver_settings.PDLPSolverMode( - solver_config.pdlp_solver_mode - ), - ) - if solver_config.method is not None: - solver_settings.set_parameter( - CUOPT_METHOD, - linear_programming.solver_settings.SolverMethod( - solver_config.method - ), - ) - if solver_config.crossover is not None: - solver_settings.set_parameter( - CUOPT_CROSSOVER, solver_config.crossover - ) + for param in solver_params: + param_value = None + if param.endswith("tolerance"): + param_value = getattr(solver_config.tolerances, param) + else: + param_value = getattr(solver_config, param) + if param_value is not None and param_value != "": + solver_settings.set_parameter(param, param_value) + + if LP_data.solver_config is not None: + solver_config = LP_data.solver_config + try: lp_time_limit = float(os.environ.get("CUOPT_LP_TIME_LIMIT_SEC")) except Exception: @@ -230,7 +180,7 @@ def create_solver(LP_data, warmstart_data): time_limit = solver_config.time_limit if time_limit is not None: logging.debug(f"setting LP time limit to {time_limit}sec") - solver_settings.set_parameter(CUOPT_TIME_LIMIT, time_limit) + solver_settings.set_parameter("time_limit", time_limit) try: lp_iteration_limit = int( @@ -248,179 +198,14 @@ def create_solver(LP_data, warmstart_data): iteration_limit = solver_config.iteration_limit if iteration_limit is not None: logging.debug(f"setting LP iteration limit to {iteration_limit}") - solver_settings.set_parameter( - CUOPT_ITERATION_LIMIT, iteration_limit - ) + solver_settings.set_parameter("iteration_limit", iteration_limit) - if solver_config.tolerances is not None: - tolerance = solver_config.tolerances - if tolerance.optimality is not None: - solver_settings.set_optimality_tolerance(tolerance.optimality) - if tolerance.absolute_dual_tolerance is not None: - solver_settings.set_parameter( - CUOPT_ABSOLUTE_DUAL_TOLERANCE, - tolerance.absolute_dual_tolerance, - ) - if tolerance.absolute_primal_tolerance is not None: - solver_settings.set_parameter( - CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, - tolerance.absolute_primal_tolerance, - ) - if tolerance.absolute_gap_tolerance is not None: - solver_settings.set_parameter( - CUOPT_ABSOLUTE_GAP_TOLERANCE, - tolerance.absolute_gap_tolerance, - ) - if tolerance.relative_dual_tolerance is not None: - solver_settings.set_parameter( - CUOPT_RELATIVE_DUAL_TOLERANCE, - tolerance.relative_dual_tolerance, - ) - if tolerance.relative_primal_tolerance is not None: - solver_settings.set_parameter( - CUOPT_RELATIVE_PRIMAL_TOLERANCE, - tolerance.relative_primal_tolerance, - ) - if tolerance.relative_gap_tolerance is not None: - solver_settings.set_parameter( - CUOPT_RELATIVE_GAP_TOLERANCE, - tolerance.relative_gap_tolerance, - ) - if tolerance.primal_infeasible_tolerance is not None: - solver_settings.set_parameter( - CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, - tolerance.primal_infeasible_tolerance, - ) - if tolerance.dual_infeasible_tolerance is not None: - solver_settings.set_parameter( - CUOPT_DUAL_INFEASIBLE_TOLERANCE, - tolerance.dual_infeasible_tolerance, - ) - if tolerance.mip_integrality_tolerance is not None: - solver_settings.set_parameter( - CUOPT_MIP_INTEGRALITY_TOLERANCE, - tolerance.mip_integrality_tolerance, - ) - if tolerance.mip_absolute_gap is not None: - solver_settings.set_parameter( - CUOPT_MIP_ABSOLUTE_GAP, tolerance.mip_absolute_gap - ) - if tolerance.mip_relative_gap is not None: - solver_settings.set_parameter( - CUOPT_MIP_RELATIVE_GAP, tolerance.mip_relative_gap - ) - if tolerance.mip_absolute_tolerance is not None: - solver_settings.set_parameter( - CUOPT_MIP_ABSOLUTE_TOLERANCE, - tolerance.mip_absolute_tolerance, - ) - if tolerance.mip_relative_tolerance is not None: - solver_settings.set_parameter( - CUOPT_MIP_RELATIVE_TOLERANCE, - tolerance.mip_relative_tolerance, - ) if warmstart_data is not None: solver_settings.set_pdlp_warm_start_data(warmstart_data) - if solver_config.mip_scaling is not None: - solver_settings.set_parameter( - CUOPT_MIP_SCALING, solver_config.mip_scaling - ) - if solver_config.mip_heuristics_only is not None: - solver_settings.set_parameter( - CUOPT_MIP_HEURISTICS_ONLY, solver_config.mip_heuristics_only - ) - if solver_config.num_cpu_threads is not None: - solver_settings.set_parameter( - CUOPT_NUM_CPU_THREADS, solver_config.num_cpu_threads - ) - if solver_config.num_gpus is not None: - solver_settings.set_parameter( - CUOPT_NUM_GPUS, solver_config.num_gpus - ) - if solver_config.crossover is not None: - solver_settings.set_parameter( - CUOPT_CROSSOVER, solver_config.crossover - ) - - def is_mip(var_types): - if var_types is None or len(var_types) == 0: - return False - elif "I" in var_types: - return True - - return False - - if solver_config.presolve is None: - if is_mip(LP_data.variable_types): - solver_config.presolve = True - else: - solver_config.presolve = False - - if solver_config.presolve is not None: - solver_settings.set_parameter( - CUOPT_PRESOLVE, solver_config.presolve - ) - - if solver_config.dual_postsolve is not None: - solver_settings.set_parameter( - CUOPT_DUAL_POSTSOLVE, solver_config.dual_postsolve - ) - if solver_config.log_to_console is not None: - solver_settings.set_parameter( - CUOPT_LOG_TO_CONSOLE, solver_config.log_to_console - ) - if solver_config.strict_infeasibility is not None: - solver_settings.set_parameter( - CUOPT_STRICT_INFEASIBILITY, solver_config.strict_infeasibility - ) if solver_config.user_problem_file != "": warnings.append(ignored_warning("user_problem_file")) - if solver_config.per_constraint_residual is not None: - solver_settings.set_parameter( - CUOPT_PER_CONSTRAINT_RESIDUAL, - solver_config.per_constraint_residual, - ) - if solver_config.save_best_primal_so_far is not None: - solver_settings.set_parameter( - CUOPT_SAVE_BEST_PRIMAL_SO_FAR, - solver_config.save_best_primal_so_far, - ) - if solver_config.first_primal_feasible is not None: - solver_settings.set_parameter( - CUOPT_FIRST_PRIMAL_FEASIBLE, - solver_config.first_primal_feasible, - ) - if solver_config.log_file != "": - solver_settings.set_parameter( - CUOPT_LOG_FILE, solver_config.log_file - ) - if solver_config.augmented is not None: - solver_settings.set_parameter( - CUOPT_AUGMENTED, solver_config.augmented - ) - if solver_config.folding is not None: - solver_settings.set_parameter(CUOPT_FOLDING, solver_config.folding) - if solver_config.dualize is not None: - solver_settings.set_parameter(CUOPT_DUALIZE, solver_config.dualize) - if solver_config.ordering is not None: - solver_settings.set_parameter( - CUOPT_ORDERING, solver_config.ordering - ) - if solver_config.barrier_dual_initial_point is not None: - solver_settings.set_parameter( - CUOPT_BARRIER_DUAL_INITIAL_POINT, - solver_config.barrier_dual_initial_point, - ) - if solver_config.eliminate_dense_columns is not None: - solver_settings.set_parameter( - CUOPT_ELIMINATE_DENSE_COLUMNS, - solver_config.eliminate_dense_columns, - ) - if solver_config.cudss_deterministic is not None: - solver_settings.set_parameter( - CUOPT_CUDSS_DETERMINISTIC, solver_config.cudss_deterministic - ) + if solver_config.solution_file != "": warnings.append(ignored_warning("solution_file"))