Skip to content

Comments

update solver_settings#840

Merged
rapids-bot[bot] merged 13 commits intoNVIDIA:release/26.02from
Iroy30:update_solver_settings_propagation_26.02
Feb 10, 2026
Merged

update solver_settings#840
rapids-bot[bot] merged 13 commits intoNVIDIA:release/26.02from
Iroy30:update_solver_settings_propagation_26.02

Conversation

@Iroy30
Copy link
Member

@Iroy30 Iroy30 commented Feb 9, 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

    • Runtime discovery of available solver parameter names exposed to language bindings.
  • Breaking Changes

    • MIP callback interface simplified — removed user_data argument.
    • Static per-parameter constants replaced by dynamic parameter names; legacy constant keys replaced by string keys (e.g., "time_limit", "iteration_limit").
  • Improved Interfaces

    • Unified, dynamic parameter handling and centralized configuration.
    • Callback flow consolidated to a single solution-reporting path; solution emission simplified.

@Iroy30 Iroy30 requested review from a team as code owners February 9, 2026 17:34
@copy-pr-bot
Copy link

copy-pr-bot bot commented Feb 9, 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 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a public accessor returning all solver parameter names; removes the user_data argument from set_mip_callback() across C++/Python layers; replaces many static Python CUOPT_* constants with a runtime-discovered solver_params list and adapts parameter handling and callbacks accordingly.

Changes

Cohort / File(s) Summary
C++ header
cpp/include/cuopt/linear_programming/solver_settings.hpp
Declared const std::vector<std::string> get_parameter_names() const; in solver_settings_t.
C++ implementation
cpp/src/math_optimization/solver_settings.cu
Implemented get_parameter_names() aggregating names from int/float/bool/string parameter containers.
Cython/PXD binding
python/cuopt/cuopt/linear_programming/solver/solver.pxd
Exposed vector[string] get_parameter_names() in the extern block (bindings updated to reflect new method).
Python parameter exposure
python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx
Added get_solver_parameter_names() and module-level solver_params; replaced many static CUOPT_* constant exports with dynamic runtime population of CUOPT_... globals; added libcpp.vector import.
Python solver settings API
python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py
Switched to dynamic solver_params for validation/iteration; changed set_mip_callback(self, callback) signature (removed user_data); updated tolerance handling and toDict to iterate solver_params.
Server-side integration
python/cuopt_server/cuopt_server/utils/linear_programming/solver.py
Removed explicit CUOPT_* imports and CustomSetSolutionCallback; centralized parameter application loop using solver_params; simplified callback to get_solution(self, solution, solution_cost); removed incumbent_set_solutions from solve() signature and simplified solution emission.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • None provided.

Suggested labels

breaking, improvement, mip

Suggested reviewers

  • rgsl888prabhu
  • gforsyth
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'update solver_settings' is vague and generic, using non-descriptive language that fails to convey the scope or nature of the changes despite substantial modifications across multiple files and subsystems. Use a more specific title that highlights the main change, such as 'Refactor solver parameter handling with dynamic parameter names exposure' or 'Replace static parameter constants with dynamic runtime-driven approach'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
🧪 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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/cuopt_server/cuopt_server/utils/linear_programming/solver.py (1)

321-326: ⚠️ Potential issue | 🟠 Major

set_mip_callback(None) appends None to the callbacks list.

When intermediate_sender is None, callback is None (line 324). Calling solver_settings.set_mip_callback(None) appends None to self.mip_callbacks (line 262 of solver_settings.py). Downstream when these callbacks are iterated and passed to the C++ layer, a None entry could cause a crash or unexpected behavior.

Guard the call:

Suggested fix
             callback = (
                 CustomGetSolutionCallback(intermediate_sender, reqId)
                 if intermediate_sender is not None
                 else None
             )
-            solver_settings.set_mip_callback(callback)
+            if callback is not None:
+                solver_settings.set_mip_callback(callback)
🤖 Fix all issues with AI agents
In `@cpp/src/math_optimization/solver_settings.cu`:
- Around line 109-110: The CUOPT_PRESOLVE parameter was accidentally changed
back to a bool entry, breaking the presolver_t enum and Python
set_parameter(int) callers; restore CUOPT_PRESOLVE as an integer/enum parameter
(not bool) by changing the entries that reference CUOPT_PRESOLVE in
solver_settings.cu so they point at the presolve/presolve fields typed as
presolver_t (or an int) and are registered in the int_parameters table (not
bool_parameters); ensure symbols referenced are CUOPT_PRESOLVE,
pdlp_settings.presolve, mip_settings.presolve, presolver_t and that Python
set_parameter(name, int) and downstream consumers (CLI, server) use the enum
integer values (Default/None/Papilo/PSLP) consistently.

In `@python/cuopt_server/cuopt_server/utils/linear_programming/solver.py`:
- Around line 126-134: The loop over solver_params can raise AttributeError when
a C++ parameter name isn't present on SolverConfig or SolverConfig.tolerances;
change the getattr calls used when reading param_value in that loop to provide a
safe default (e.g., None) so missing attributes return None instead of raising,
i.e., use getattr(solver_config.tolerances, param, None) for tolerance names and
getattr(solver_config, param, None) for others before calling
solver_settings.set_parameter; keep the existing check that skips None/empty
values so only present parameters are set.

In `@python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py`:
- Around line 282-293: The toDict method currently only converts positive
infinity to None (param_value == float("inf")), leaving float("-inf") unchanged;
update to detect any infinity by using math.isinf on values returned from
get_parameter and set param_value to None for both +inf and -inf (ensure to
import math), and guard the check so non-numeric parameter values don't
raise—apply this change inside the loop over solver_params where solver_config
and param_value are handled in toDict.
- Around line 178-180: The loop in set_optimality_tolerance currently updates
every param in solver_params that endswith("tolerance") and not
startswith("mip"), which incorrectly changes infeasibility detection thresholds
like primal_infeasible_tolerance and dual_infeasible_tolerance; change the logic
to only update the intended optimality tolerances (e.g., list the six optimality
keys explicitly) or at minimum exclude any parameter name containing
"infeasible"/"infeasibility" before assigning eps_optimal to
self.settings_dict[param]; ensure references to solver_params,
self.settings_dict, and eps_optimal remain unchanged and only the matching
condition is tightened.
🧹 Nitpick comments (4)
cpp/include/cuopt/linear_programming/solver_settings.hpp (1)

96-96: const on a return-by-value inhibits move semantics.

Returning const std::vector<std::string> by value prevents the compiler from applying move semantics at the call site. Drop the const qualifier on the return type.

Suggested fix
-  const std::vector<std::string> get_parameter_names() const;
+  std::vector<std::string> get_parameter_names() const;
cpp/src/math_optimization/solver_settings.cu (1)

442-459: get_parameter_names() returns duplicate names for shared parameters.

Parameters like CUOPT_TIME_LIMIT, CUOPT_NUM_GPUS, CUOPT_LOG_TO_CONSOLE, CUOPT_PRESOLVE, CUOPT_LOG_FILE, CUOPT_SOLUTION_FILE, and CUOPT_USER_PROBLEM_FILE each appear twice (once for pdlp_settings, once for mip_settings). The returned vector will contain these duplicates, which propagates to the Python solver_params list causing redundant work in all loops that iterate over it. Consider deduplicating:

Suggested fix — deduplicate using a set
 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> 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;
+  std::set<std::string> seen;
+  std::vector<std::string> parameter_names;
+  auto insert = [&](const std::string& name) {
+    if (seen.insert(name).second) { parameter_names.push_back(name); }
+  };
+  for (auto& param : int_parameters) { insert(param.param_name); }
+  for (auto& param : float_parameters) { insert(param.param_name); }
+  for (auto& param : bool_parameters) { insert(param.param_name); }
+  for (auto& param : string_parameters) { insert(param.param_name); }
+  return parameter_names;
 }
python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx (1)

44-45: solver_params will be a list — consider converting to a unique ordered collection.

Since get_parameter_names() in C++ can return duplicate names (parameters shared between PDLP and MIP settings), solver_params may contain duplicates. This list is used for membership tests (name not in solver_params) in solver_settings.py and for iteration in toDict/set_optimality_tolerance. Consider deduplicating here (or in C++, per the comment on solver_settings.cu):

solver_params = list(dict.fromkeys(get_solver_parameter_names()))

Also, globals()["CUOPT_"+param.upper()] = param dynamically sets module attributes that won't be visible to static analysis or IDE autocompletion. This is a trade-off of the dynamic approach — worth documenting.

python/cuopt_server/cuopt_server/utils/linear_programming/solver.py (1)

135-136: Redundant solver_config re-check and re-assignment.

Lines 124–125 already check LP_data.solver_config is not None and assign solver_config. The identical check at lines 135–136 is unnecessary since we're still within the same function scope.

Suggested fix — merge into one block
     if LP_data.solver_config is not None:
         solver_config = LP_data.solver_config
         for param in solver_params:
             ...
 
-    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 282 to 293
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

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

toDict doesn't handle negative infinity.

Line 290 checks param_value == float("inf") and converts to None, but float("-inf") would pass through as-is, which may not serialize correctly (e.g., JSON doesn't support -Infinity). Consider checking with math.isinf(param_value) instead.

Suggested fix
+import math
+
 ...
-                if param_value == float("inf"):
+                if isinstance(param_value, float) and math.isinf(param_value):
                     param_value = None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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
def toDict(self):
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 isinstance(param_value, float) and math.isinf(param_value):
param_value = None
solver_config[param] = param_value
🤖 Prompt for AI Agents
In `@python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py`
around lines 282 - 293, The toDict method currently only converts positive
infinity to None (param_value == float("inf")), leaving float("-inf") unchanged;
update to detect any infinity by using math.isinf on values returned from
get_parameter and set param_value to None for both +inf and -inf (ensure to
import math), and guard the check so non-numeric parameter values don't
raise—apply this change inside the loop over solver_params where solver_config
and param_value are handled in toDict.

@Iroy30 Iroy30 added non-breaking Introduces a non-breaking change improvement Improves an existing functionality labels Feb 9, 2026
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: 1

🤖 Fix all issues with AI agents
In `@cpp/src/math_optimization/solver_settings.cu`:
- Around line 452-469: The get_parameter_names() method returns duplicate names
because entries from int_parameters, float_parameters, bool_parameters and
string_parameters can overlap; update
solver_settings_t<i_t,f_t>::get_parameter_names() to collect names into a
std::set (or unordered_set) while iterating those vectors (int_parameters,
float_parameters, bool_parameters, string_parameters) and then return a
deduplicated std::vector<std::string> built from that set so callers receive
unique parameter names.

@Iroy30
Copy link
Member Author

Iroy30 commented Feb 9, 2026

/ok to test a756c8a

@Iroy30
Copy link
Member Author

Iroy30 commented Feb 9, 2026

/ok to test 56ed4c4

@Iroy30
Copy link
Member Author

Iroy30 commented Feb 9, 2026

/ok to test a9b94f6

@Iroy30
Copy link
Member Author

Iroy30 commented Feb 9, 2026

/ok to test 96c1109

Copy link
Collaborator

@rgsl888prabhu rgsl888prabhu left a comment

Choose a reason for hiding this comment

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

Are we using these in any docs which might break it ?

@Iroy30
Copy link
Member Author

Iroy30 commented Feb 9, 2026

/ok to test 5d8ebaf

@Iroy30
Copy link
Member Author

Iroy30 commented Feb 9, 2026

Are we using these in any docs which might break it ?

No docs will not break, user usage is exactly as before.

Copy link
Contributor

@chris-maes chris-maes left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks @Iroy30 . Glad I'll never have to add another parameter name in to the cython file after this PR merges :)

@Iroy30
Copy link
Member Author

Iroy30 commented Feb 10, 2026

/merge

@rapids-bot rapids-bot bot merged commit ce696eb into NVIDIA:release/26.02 Feb 10, 2026
196 of 197 checks passed
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.

3 participants