Skip to content

remove explicit solver settings in python#829

Open
Iroy30 wants to merge 2 commits intoNVIDIA:mainfrom
Iroy30:update_python_solver_parameters
Open

remove explicit solver settings in python#829
Iroy30 wants to merge 2 commits intoNVIDIA:mainfrom
Iroy30:update_python_solver_parameters

Conversation

@Iroy30
Copy link
Member

@Iroy30 Iroy30 commented Feb 5, 2026

Description

Issue

Checklist

  • I am familiar with the Contributing Guidelines.
  • Testing
    • New or existing tests cover these changes
    • Added tests
    • Created an issue to follow-up
    • NA
  • Documentation
    • The documentation is up to date with these changes
    • Added new documentation
    • NA

Summary by CodeRabbit

  • New Features

    • Dynamically retrieve and expose all available solver parameter names.
    • Runtime-export of parameter name constants for easier programmatic access.
    • Validation added for parameter get/set operations to surface invalid names.
  • Refactor

    • Unified and simplified solver parameter handling across Python and server code, replacing many hard-coded parameter paths with a single iterable-driven approach.

@Iroy30 Iroy30 requested review from a team as code owners February 5, 2026 15:23
@copy-pr-bot
Copy link

copy-pr-bot bot commented Feb 5, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

Adds a new get_parameter_names() API to solver_settings in C++, exposes it via Cython, and refactors Python layers to discover and iterate solver parameters dynamically instead of using hardcoded constants.

Changes

Cohort / File(s) Summary
C++ core
cpp/include/cuopt/linear_programming/solver_settings.hpp, cpp/src/math_optimization/solver_settings.cu
Declared and implemented solver_settings_t::get_parameter_names() const to return a consolidated list of parameter names from int/float/bool/string parameter collections.
Cython binding
python/cuopt/cuopt/linear_programming/solver/solver.pxd, python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx
Exposed get_parameter_names() in the extern class and added a cpdef to retrieve vector[string], convert to a Python list, and dynamically create module-level CUOPT_<NAME> constants.
Python client settings
python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py
Replaced many hardcoded solver parameter constants with a dynamic solver_params iteration; added validation in get/set parameter helpers and simplified toDict/set_optimality_tolerance logic to use the discovered parameter list.
Server-side solver util
python/cuopt_server/cuopt_server/utils/linear_programming/solver.py
Removed large explicit per-parameter handling in favor of looping over solver_params; consolidated time/iteration limit handling and removed numerous tolerance-specific branches and individual setters.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'remove explicit solver settings in python' clearly and concisely describes the main change: replacing hardcoded solver parameter constants with dynamic generation from the solver_settings_t object in Python code.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@cpp/src/math_optimization/solver_settings.cu`:
- Around line 442-459: The get_parameter_names method duplicates iteration over
int_parameters, float_parameters, bool_parameters, and string_parameters and
collects duplicate names; create a shared helper (e.g., a template function
aggregate_parameter_names or for_each_parameter in the solver_settings_t class)
that accepts a callable or output container and iterates the four parameter
vectors once to append names, using an unordered_set to filter duplicates;
replace solver_settings_t<i_t,f_t>::get_parameter_names and other similar
methods with calls to this helper (referencing int_parameters, float_parameters,
bool_parameters, string_parameters and the helper name) so all aggregation logic
and deduplication live in one place.

In `@python/cuopt_server/cuopt_server/utils/linear_programming/solver.py`:
- Around line 126-133: The loop using getattr on solver_config and
solver_config.tolerances can raise AttributeError if an attribute is missing;
update the loop (referencing solver_params, solver_config,
solver_config.tolerances and solver_settings.set_parameter) to call getattr with
a safe default (e.g., None) and/or check attribute existence before accessing so
missing parameters are skipped gracefully; ensure you only call
solver_settings.set_parameter when the retrieved param_value is not None and not
an empty string.
🧹 Nitpick comments (3)
cpp/include/cuopt/linear_programming/solver_settings.hpp (1)

96-96: Consider adding a documentation comment for the new public API method.

Other public methods in this class would benefit from Doxygen-style documentation. Since this is a public header, consider adding a brief description of what get_parameter_names() returns (e.g., aggregated list of all parameter names from int, float, bool, and string parameters).

The return-by-value (vs. const reference like lines 92-95) is appropriate here since the implementation constructs a new vector.

python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx (1)

44-45: Dynamic constant generation pattern is functional but worth documenting.

This pattern creates module-level constants (e.g., CUOPT_TIME_LIMIT = "time_limit") at import time, which is a reasonable approach for dynamic parameter discovery. However:

  1. The one-liner is dense and could benefit from a brief comment explaining the intent.
  2. If get_solver_parameter_names() returns duplicates (as noted in the C++ review), duplicate assignments will occur (harmless but wasteful).
✏️ Suggested improvement for clarity
 solver_params = get_solver_parameter_names()
-for param in solver_params: globals()["CUOPT_"+param.upper()] = param
+# Export module-level constants CUOPT_<PARAM_NAME> for each solver parameter
+for param in solver_params:
+    globals()["CUOPT_" + param.upper()] = param
python/cuopt_server/cuopt_server/utils/linear_programming/solver.py (1)

135-136: Redundant None check for LP_data.solver_config.

This condition is already checked at line 124. The nested block (lines 135-178) is already inside the if LP_data.solver_config is not None: block, making this check redundant.

♻️ Remove redundant check
             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"))

Comment on lines +442 to +459
template <typename i_t, typename f_t>
const std::vector<std::string> solver_settings_t<i_t, f_t>::get_parameter_names() const
{
std::vector<std::string> 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;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check the parameter definitions to see if duplicates exist
head -n 200 cpp/src/math_optimization/solver_settings.cu | tail -n 140

Repository: NVIDIA/cuopt

Length of output: 7060


🏁 Script executed:

#!/bin/bash
# Search for get_parameter_names usage
rg -n "get_parameter_names" --type=cpp --type=py -B2 -A5

Repository: NVIDIA/cuopt

Length of output: 866


🏁 Script executed:

#!/bin/bash
# Look for similar iteration patterns in the file to assess code duplication
rg -n "for \(auto& param : .*_parameters\)" cpp/src/math_optimization/solver_settings.cu

Repository: NVIDIA/cuopt

Length of output: 961


🏁 Script executed:

#!/bin/bash
# Search for Python usage of get_parameter_names
find . -name "*.py" -type f | head -20

Repository: NVIDIA/cuopt

Length of output: 1222


🏁 Script executed:

#!/bin/bash
# Search for get_parameter_names in all Python files
rg "get_parameter_names" . --type=py -B3 -A10

Repository: NVIDIA/cuopt

Length of output: 38


🏁 Script executed:

#!/bin/bash
# Check if duplicates in returned vector would be problematic
# Look at how parameter names are used (e.g., in loops, sets, dicts)
rg "get_parameter_names\(\)" . -A15 --type=cpp --type=py | head -60

Repository: NVIDIA/cuopt

Length of output: 1267


Refactor repeated iteration patterns over parameter types into a shared helper function.

The returned vector contains duplicate parameter names (e.g., CUOPT_TIME_LIMIT, CUOPT_NUM_GPUS, CUOPT_LOG_TO_CONSOLE, CUOPT_PRESOLVE, CUOPT_LOG_FILE, CUOPT_SOLUTION_FILE, CUOPT_USER_PROBLEM_FILE appear across multiple parameter vectors). More importantly, the pattern of iterating over int_parameters, float_parameters, bool_parameters, and string_parameters is duplicated 5+ times throughout this file (lines 131–299 alone contain multiple occurrences). Per coding guidelines, 3+ code duplications in solver components should be refactored into shared utilities.

Create a helper template that accepts a callable or uses a generic aggregation pattern to avoid repeating the four-way loop. This improves maintainability and allows deduplication to be handled consistently across all parameter aggregation methods.

🤖 Prompt for AI Agents
In `@cpp/src/math_optimization/solver_settings.cu` around lines 442 - 459, The
get_parameter_names method duplicates iteration over int_parameters,
float_parameters, bool_parameters, and string_parameters and collects duplicate
names; create a shared helper (e.g., a template function
aggregate_parameter_names or for_each_parameter in the solver_settings_t class)
that accepts a callable or output container and iterates the four parameter
vectors once to append names, using an unordered_set to filter duplicates;
replace solver_settings_t<i_t,f_t>::get_parameter_names and other similar
methods with calls to this helper (referencing int_parameters, float_parameters,
bool_parameters, string_parameters and the helper name) so all aggregation logic
and deduplication live in one place.

Comment on lines +126 to +133
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)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential AttributeError if solver_config lacks expected attributes.

This loop assumes solver_config (or solver_config.tolerances) has an attribute for every parameter in solver_params. If the server's config schema doesn't define all parameters, getattr() will raise AttributeError.

Consider using getattr() with a default value to handle missing attributes gracefully:

🛡️ Proposed fix to handle missing attributes
         for param in solver_params:
             param_value = None
             if param.endswith("tolerance"):
-                param_value = getattr(solver_config.tolerances, param)
+                param_value = getattr(solver_config.tolerances, param, None)
             else:
-                param_value = getattr(solver_config, param)
+                param_value = getattr(solver_config, param, None)
             if param_value is not None and param_value != "":
                 solver_settings.set_parameter(param, param_value)
🤖 Prompt for AI Agents
In `@python/cuopt_server/cuopt_server/utils/linear_programming/solver.py` around
lines 126 - 133, The loop using getattr on solver_config and
solver_config.tolerances can raise AttributeError if an attribute is missing;
update the loop (referencing solver_params, solver_config,
solver_config.tolerances and solver_settings.set_parameter) to call getattr with
a safe default (e.g., None) and/or check attribute existence before accessing so
missing parameters are skipped gracefully; ensure you only call
solver_settings.set_parameter when the retrieved param_value is not None and not
an empty string.

@Iroy30 Iroy30 added non-breaking Introduces a non-breaking change improvement Improves an existing functionality labels Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improves an existing functionality non-breaking Introduces a non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant