Skip to content

change semantics of problem construction in cuOptReadProblem#1559

Open
tmckayus wants to merge 1 commit into
NVIDIA:mainfrom
tmckayus:feature/read-problem-adopt
Open

change semantics of problem construction in cuOptReadProblem#1559
tmckayus wants to merge 1 commit into
NVIDIA:mainfrom
tmckayus:feature/read-problem-adopt

Conversation

@tmckayus

Copy link
Copy Markdown
Contributor

Since the mps data model is constructed internally, use move semantics when building a CPU problem to avoid an unnecessary copy. This is a small optimization but on a large problem with 10M variables and 200k constraints, it can save half a second in problem creation.

since the mps data model is constructed internally, use move
semantics when building a CPU problem to avoid a copy
@tmckayus tmckayus requested a review from a team as a code owner July 10, 2026 20:50
@tmckayus tmckayus added non-breaking Introduces a non-breaking change improvement Improves an existing functionality labels Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

CPU-backed optimization problems now adopt parsed MPS/QPS storage without copying, while device-backed problems retain population fallback. Reader call sites use the new path, solution parsing uses problem-owned variable names, and tests compare adoption with population.

Changes

MPS Data Model Adoption

Layer / File(s) Summary
Adoption contracts and backend dispatch
cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp, cpp/include/cuopt/mathematical_optimization/optimization_problem_utils.hpp
Adds the CPU adoption API and dispatches CPU targets to move-based adoption while retaining population for other backends.
CPU model transfer and classification
cpp/src/pdlp/cpu_optimization_problem.cpp
Moves model fields and quadratic constraints into CPU problems, converts variable types, derives problem categories, and resets the source model.
Reader integration and equivalence validation
cpp/src/pdlp/cuopt_c.cpp, cpp/cuopt_cli.cpp, cpp/tests/linear_programming/unit_tests/solution_interface_test.cu
Updates readers to adopt parsed models, obtains solution variable names from the created problem, and verifies adoption matches population for linear and quadratic cases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: kaatish, mlubin, akifcorduk, ramakrishnap-nv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title references the changed problem-construction behavior in cuOptReadProblem, which matches the main move-semantics update.
Description check ✅ Passed The description matches the PR’s optimization goal of using move semantics to avoid copying the internally created MPS data model.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
cpp/tests/linear_programming/unit_tests/solution_interface_test.cu (1)

495-517: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

No test case exercises a non-empty quadratic objective through the adopt path.

expect_adopt_matches_populate already asserts on get_quadratic_objective_values/indices/offsets() (lines 471-473), but neither adopt_matches_populate_lp_file nor adopt_matches_populate_quadratic_constraints supplies a model with a quadratic objective — only quadratic constraints are covered. The Q_values_/Q_indices_/Q_offsets_ move branch in cpu_optimization_problem_t::adopt_from_mps_data_model (cpp_optimization_problem.cpp lines 1165-1167) is therefore untested against the populate baseline.

As per path instructions for cpp/tests/**, tests should validate numerical correctness for the specific paths under test rather than just "runs without error." Want me to draft a QP-objective test case (e.g. read_lp_from_string with a quadratic objective term) reusing expect_adopt_matches_populate?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/tests/linear_programming/unit_tests/solution_interface_test.cu` around
lines 495 - 517, Add a test in SolutionInterfaceTest that builds a model with a
non-empty quadratic objective using read_lp_from_string, then passes it to
expect_adopt_matches_populate. Keep the existing quadratic-constraint coverage
and ensure the new case exercises the quadratic objective values, indices, and
offsets comparison through the adopt path.

Source: Path instructions

cpp/src/pdlp/cpu_optimization_problem.cpp (2)

1139-1183: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Adopt path bypasses the validating setters used by the populate path.

adopt_from_mps_data_model assigns A_, Q_values_/Q_indices_/Q_offsets_, etc. directly via std::move, skipping the cuopt_expects null/size checks that set_csr_constraint_matrix / set_quadratic_objective_matrix (and populate_from_mps_data_model) perform. This is presumably fine since mps_data_model_t is already internally consistent after parsing, but it means a parser defect would no longer be caught here and could surface later as an out-of-bounds read (e.g. indexing Q_offsets_[n_vars] downstream).

Consider adding lightweight invariant checks (e.g. cuopt_expects on offsets/size consistency) mirroring what the setters enforce, so a parser bug fails fast instead of propagating silently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/pdlp/cpu_optimization_problem.cpp` around lines 1139 - 1183,
adopt_from_mps_data_model bypasses validation performed by the matrix setters,
allowing malformed CSR data to propagate. Add lightweight cuopt_expects
invariant checks in this method for A_ and Q_values_/Q_indices_/Q_offsets_
nullability, offset lengths, and index/value size consistency, mirroring
set_csr_constraint_matrix and set_quadratic_objective_matrix before downstream
use.

1114-1135: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use explicit field assignment here. The two quadratic_constraint_t layouts match today, but this positional aggregate init is coupled to both declarations and can break silently if either is reordered. add_quadratic_constraint already uses named assignments, so mirroring that pattern here would be safer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/pdlp/cpu_optimization_problem.cpp` around lines 1114 - 1135, Replace
the positional aggregate initialization in move_quadratic_constraints_from_model
with explicit field-by-field assignments, mirroring the named-assignment pattern
used by add_quadratic_constraint. Populate each destination
quadratic_constraint_t member from qc, preserving move semantics for strings and
vectors, then append the fully initialized object to converted.
cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp (1)

85-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Doc comment missing @param tag.

The brief and moved-from note are good, but there's no @param describing data_model, unlike the free-function counterpart in optimization_problem_utils.hpp which documents @tparam/@param fully.

As per path instructions, "New public functions/classes need Doxygen-style documentation" and "Verify parameter descriptions match actual types/behavior" for public C++ headers under cpp/include/cuopt/**/*.

📝 Proposed doc addition
   /**
    * `@brief` Transfer parsed MPS/QPS storage into this CPU problem without copying array/string data.
    *
    * The model is left in a moved-from state and must not be used afterward.
+   *
+   * `@param`[in,out] data_model Parsed MPS/QPS model to adopt; left moved-from on return.
    */
   void adopt_from_mps_data_model(io::mps_data_model_t<i_t, f_t>&& data_model);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp`
around lines 85 - 91, Add a Doxygen `@param` description for the rvalue-reference
parameter data_model in adopt_from_mps_data_model, stating that it is the parsed
MPS/QPS storage being adopted without copying and is left moved-from afterward;
keep the documentation consistent with the corresponding free function.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp`:
- Around line 85-91: Add a Doxygen `@param` description for the rvalue-reference
parameter data_model in adopt_from_mps_data_model, stating that it is the parsed
MPS/QPS storage being adopted without copying and is left moved-from afterward;
keep the documentation consistent with the corresponding free function.

In `@cpp/src/pdlp/cpu_optimization_problem.cpp`:
- Around line 1139-1183: adopt_from_mps_data_model bypasses validation performed
by the matrix setters, allowing malformed CSR data to propagate. Add lightweight
cuopt_expects invariant checks in this method for A_ and
Q_values_/Q_indices_/Q_offsets_ nullability, offset lengths, and index/value
size consistency, mirroring set_csr_constraint_matrix and
set_quadratic_objective_matrix before downstream use.
- Around line 1114-1135: Replace the positional aggregate initialization in
move_quadratic_constraints_from_model with explicit field-by-field assignments,
mirroring the named-assignment pattern used by add_quadratic_constraint.
Populate each destination quadratic_constraint_t member from qc, preserving move
semantics for strings and vectors, then append the fully initialized object to
converted.

In `@cpp/tests/linear_programming/unit_tests/solution_interface_test.cu`:
- Around line 495-517: Add a test in SolutionInterfaceTest that builds a model
with a non-empty quadratic objective using read_lp_from_string, then passes it
to expect_adopt_matches_populate. Keep the existing quadratic-constraint
coverage and ensure the new case exercises the quadratic objective values,
indices, and offsets comparison through the adopt path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d3d99f78-2913-436c-9b2b-fed8f8d760fc

📥 Commits

Reviewing files that changed from the base of the PR and between 940ec59 and dab644e.

📒 Files selected for processing (6)
  • cpp/cuopt_cli.cpp
  • cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem_utils.hpp
  • cpp/src/pdlp/cpu_optimization_problem.cpp
  • cpp/src/pdlp/cuopt_c.cpp
  • cpp/tests/linear_programming/unit_tests/solution_interface_test.cu

@github-actions

Copy link
Copy Markdown

CI Test Summary

✅ All 31 test job(s) passed.

#include <string>
#include <vector>

namespace cuopt::mathematical_optimization::io {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: move this inside the cuopt::mathematical_optimization block below with a namespace io.

Comment thread cpp/cuopt_cli.cpp

cuopt::mathematical_optimization::populate_from_mps_data_model(problem_interface.get(),
mps_data_model);
// adopt_from_mps_data_model moves the parsed storage into a HOST-backed problem and falls back to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this comment needed? It looks like it just says what the function does; any relevant details should be on the function docstring.

// adopt_from_mps_data_model must produce a problem identical to populate_from_mps_data_model.
TEST_F(SolutionInterfaceTest, adopt_matches_populate_lp_file)
{
const auto model = cuopt::mathematical_optimization::io::read_mps<int, double>(lp_file_);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
const auto model = cuopt::mathematical_optimization::io::read_mps<int, double>(lp_file_);
const auto model = io::read_mps<int, double>(lp_file_);


TEST_F(SolutionInterfaceTest, adopt_matches_populate_quadratic_constraints)
{
const auto model = cuopt::mathematical_optimization::io::read_lp_from_string<int, double>(R"LP(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
const auto model = cuopt::mathematical_optimization::io::read_lp_from_string<int, double>(R"LP(
const auto model = io::read_lp_from_string<int, double>(R"LP(

We should check why code is generated in this style and if there's anything we can do to prevent it.

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