Skip to content

Improve QC COO canonicalization performance#1516

Open
yuwenchen95 wants to merge 24 commits into
NVIDIA:mainfrom
yuwenchen95:fix-qcqp-canonical-coo
Open

Improve QC COO canonicalization performance#1516
yuwenchen95 wants to merge 24 commits into
NVIDIA:mainfrom
yuwenchen95:fix-qcqp-canonical-coo

Conversation

@yuwenchen95

Copy link
Copy Markdown
Contributor

Description

Improve QC COO canonicalization performance by reusing a scratch workspace across constraints, instead of allocating fresh lookup buffers on every call. Canonicalization output is unchanged; this is a performance optimization for batch ingest paths (MPS/LP parse, gRPC, incremental add_quadratic_constraint).

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

Store quadratic-constraint Q in canonical COO (one cross coefficient per
variable pair) and run canonicalization at ingest boundaries (MPS/LP,
C API, Python→solver, gRPC, MPS export). The RSOC fast path now accepts
a single cross term (e.g. -2*x0*x1 for ||tail||^2 <= 2*x0*x1) instead of
requiring symmetric MPS-style halves, which previously routed natural
API forms to the general QC path and produced wrong optima.
MPS QCMATRIX still accepts symmetric halves; the writer expands canonical
cross terms back to MPS form on export. Adds tests and doc updates for
C API, parser, and examples.

Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
…im redundant canonicalization comments

Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
Replace qc_coo options/wrappers with canonicalize_coo_matrix(rows,cols,vals,bool).
Use machine epsilon for zero checks; sum both orientations into one (lo,hi) entry.
Test via append_quadratic_constraint; update gRPC codegen call sites.

Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
…out only for mps files

Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
…atic cost in quadratic constraints

Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
…icalization with sort-based algorithms (adding strict duplicate-entry rejection and fail-fast validation during parsing), and update the parser tests accordingly.

Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
…st the grpc test data avoiding the effect from dropped 0 entry

Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
Signed-off-by: YUWEN Chen <yuwchen@nvidia.com>
Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>

# Conflicts:
#	cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp
#	cpp/src/grpc/grpc_problem_mapper.cpp
#	cpp/src/io/data_model_view.cpp
#	cpp/src/io/mps_parser.cpp
#	cpp/src/io/mps_writer.cpp
Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
Signed-off-by: YUWEN Chen <yuwchen@nvidia.com>
Signed-off-by: YUWEN Chen <yuwchen@nvidia.com>
@yuwenchen95 yuwenchen95 requested a review from mlubin July 3, 2026 15:29
@yuwenchen95 yuwenchen95 requested a review from a team as a code owner July 3, 2026 15:29
@yuwenchen95 yuwenchen95 added the non-breaking Introduces a non-breaking change label Jul 3, 2026
@yuwenchen95 yuwenchen95 requested a review from rg20 July 3, 2026 15:29
@yuwenchen95 yuwenchen95 added improvement Improves an existing functionality barrier labels Jul 3, 2026
Signed-off-by: YUWEN Chen <yuwchen@nvidia.com>
@yuwenchen95

Copy link
Copy Markdown
Contributor Author

/ok to test fc12d4a

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 01798379-3ae0-439c-bd76-b47befd18243

📥 Commits

Reviewing files that changed from the base of the PR and between 6decb20 and 4fa522b.

📒 Files selected for processing (1)
  • cpp/src/pdlp/cpu_optimization_problem.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • cpp/src/pdlp/cpu_optimization_problem.cpp

📝 Walkthrough

Walkthrough

Adds a reusable workspace for quadratic COO canonicalization, updates canonicalization and append APIs to take it, and threads the new parameter through parsing, writing, optimization problem code, generated conversions, and tests.

Changes

Quadratic constraint COO canonicalization workspace

Layer / File(s) Summary
Workspace type and API contract
cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp, cpp/include/cuopt/mathematical_optimization/optimization_problem_utils.hpp, cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp, cpp/include/cuopt/mathematical_optimization/optimization_problem.hpp
Defines coo_canonicalization_workspace_t<i_t, f_t>, updates canonicalize and append declarations to use it, replaces the quadratic-constraint canonicalizer declaration with a generic template, and declares the new optimization-problem workspace members and special members.
MPS parsing and model implementation
cpp/src/io/mps_parser_internal.hpp, cpp/src/io/mps_parser.cpp, cpp/src/io/mps_data_model.cpp, cpp/src/io/lp_parser.cpp, cpp/src/io/mps_writer.cpp
Reworks canonicalize_coo_matrix around the reusable workspace, updates append_quadratic_constraint to pass the workspace through, and threads a shared workspace through MPS ingestion, parser flushing, writer emission, and explicit instantiations.
Optimization problem workspace wiring
cpp/src/pdlp/cpu_optimization_problem.cpp, cpp/src/pdlp/optimization_problem.cu
Initializes reusable quadratic-canonicalization workspaces inside the optimization problem implementations and passes them into add_quadratic_constraint calls.
Generated decoding and parser tests
cpp/src/grpc/codegen/generate_conversions.py, cpp/tests/linear_programming/parser_test.cpp
Changes generated quadratic-constraint decoding to canonicalize the full collected vector once, and updates parser tests to construct and pass the workspace argument.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

  • NVIDIA/cuopt#1439: Both PRs modify the quadratic COO canonicalization pipeline around canonicalize_coo_matrix and append_quadratic_constraint.

Suggested reviewers: ramakrishnap-nv, mlubin, chris-maes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: improving QC COO canonicalization performance.
Description check ✅ Passed The description matches the changeset and accurately describes the workspace-reuse optimization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

CI Test Summary

✅ All 31 test job(s) passed.

@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.

Caution

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

⚠️ Outside diff range comments (1)
cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp (1)

328-348: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Missing @param workspace doc for the new parameter.

Every other parameter of append_quadratic_constraint is documented, but the newly-added workspace parameter has no @param entry.

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

📝 Proposed doc fix
   * `@param` constraint_row_type MPS ROWS type: 'L' (<=) or 'G' (>=). Stored as given; 'G' rows are
   *        converted to '<=' form when building the SOCP for the barrier solver. Equality ('E') is
   *        not supported.
+  * `@param` workspace Reusable scratch workspace for COO canonicalization. Safe to reuse across
+  *        successive calls (even for different constraints) but must not be shared across
+  *        concurrent invocations.
   */
🤖 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/io/mps_data_model.hpp` around
lines 328 - 348, The append_quadratic_constraint documentation is missing a
Doxygen `@param` entry for the new workspace argument. Update the comment above
append_quadratic_constraint to document workspace with its actual type
coo_canonicalization_workspace_t<i_t, f_t>& and describe that it is the reusable
canonicalization scratch space used when ingesting the quadratic COO triplets.

Source: Path instructions

🧹 Nitpick comments (2)
cpp/include/cuopt/mathematical_optimization/optimization_problem.hpp (1)

427-429: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Same thread-safety documentation suggestion as cpu_optimization_problem_t.

qc_coo_workspace_ is reused across add_quadratic_constraint calls; concurrent use on the same instance is unsafe. Worth a short doc note, consistent with the equivalent member in cpu_optimization_problem_t.

As per path instructions, cpp/include/cuopt/**/*: "Suggest documenting thread-safety, GPU requirements, numerical behavior."

🤖 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/optimization_problem.hpp` around
lines 427 - 429, Add a short thread-safety note for qc_coo_workspace_ in
optimization_problem_t, matching the documentation style used in
cpu_optimization_problem_t. Clarify that this reusable workspace is mutated
during add_quadratic_constraint calls, so concurrent use on the same
optimization_problem_t instance is unsafe and callers should serialize access or
use separate instances.

Source: Path instructions

cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp (1)

228-230: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider documenting thread-safety of the reused workspace.

qc_coo_workspace_ is shared mutable state reused across add_quadratic_constraint calls on the same instance. Concurrent calls to add_quadratic_constraint from multiple threads on the same object would race on this workspace (in addition to the existing quadratic_constraints_.push_back). A short doc comment noting the class/method is not thread-safe for concurrent mutation would help users avoid misuse.

Note: mutable is unnecessary here since add_quadratic_constraint is not const.

As per path instructions, cpp/include/cuopt/**/*: "Suggest documenting thread-safety, GPU requirements, numerical behavior."

🤖 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 228 - 230, Document that the reused workspace in
CPUOptimizationProblem is not safe for concurrent mutation, since
qc_coo_workspace_ is shared state used by add_quadratic_constraint alongside
quadratic_constraints_.push_back. Add a short comment near qc_coo_workspace_ or
add_quadratic_constraint stating the class/method is not thread-safe for
simultaneous calls on the same instance, and remove mutable from
qc_coo_workspace_ because add_quadratic_constraint is already non-const.

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.

Outside diff comments:
In `@cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp`:
- Around line 328-348: The append_quadratic_constraint documentation is missing
a Doxygen `@param` entry for the new workspace argument. Update the comment above
append_quadratic_constraint to document workspace with its actual type
coo_canonicalization_workspace_t<i_t, f_t>& and describe that it is the reusable
canonicalization scratch space used when ingesting the quadratic COO triplets.

---

Nitpick comments:
In `@cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp`:
- Around line 228-230: Document that the reused workspace in
CPUOptimizationProblem is not safe for concurrent mutation, since
qc_coo_workspace_ is shared state used by add_quadratic_constraint alongside
quadratic_constraints_.push_back. Add a short comment near qc_coo_workspace_ or
add_quadratic_constraint stating the class/method is not thread-safe for
simultaneous calls on the same instance, and remove mutable from
qc_coo_workspace_ because add_quadratic_constraint is already non-const.

In `@cpp/include/cuopt/mathematical_optimization/optimization_problem.hpp`:
- Around line 427-429: Add a short thread-safety note for qc_coo_workspace_ in
optimization_problem_t, matching the documentation style used in
cpu_optimization_problem_t. Clarify that this reusable workspace is mutated
during add_quadratic_constraint calls, so concurrent use on the same
optimization_problem_t instance is unsafe and callers should serialize access or
use separate instances.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: fc032444-fa4b-4a7d-ad4a-26c040f89054

📥 Commits

Reviewing files that changed from the base of the PR and between fe7348a and fc12d4a.

⛔ Files ignored due to path filters (2)
  • cpp/src/grpc/codegen/generated/generated_chunked_arrays_to_problem.inc is excluded by !**/generated/**
  • cpp/src/grpc/codegen/generated/generated_proto_to_problem.inc is excluded by !**/generated/**
📒 Files selected for processing (13)
  • cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp
  • cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem_utils.hpp
  • cpp/src/grpc/codegen/generate_conversions.py
  • cpp/src/io/lp_parser.cpp
  • cpp/src/io/mps_data_model.cpp
  • cpp/src/io/mps_parser.cpp
  • cpp/src/io/mps_parser_internal.hpp
  • cpp/src/io/mps_writer.cpp
  • cpp/src/pdlp/cpu_optimization_problem.cpp
  • cpp/src/pdlp/optimization_problem.cu
  • cpp/tests/linear_programming/parser_test.cpp
💤 Files with no reviewable changes (1)
  • cpp/src/io/mps_parser_internal.hpp

Comment thread cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp
Comment thread cpp/src/io/mps_parser.cpp Outdated
Comment thread cpp/src/io/mps_parser.cpp Outdated
Comment thread cpp/src/io/mps_parser.cpp Outdated
Comment thread cpp/src/io/mps_parser.cpp Outdated
for (size_t p = row_output_start; p < row_output_end; ++p) {
workspace.col_position_[entries[p].col] = workspace_t::invalid_index;
}
std::sort(entries.begin() + row_output_start,

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.

Do we actually need columns to be sorted within rows?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same reason as for the sorting on rows.

Comment thread cpp/src/io/mps_parser.cpp Outdated
} else {
entries[static_cast<size_t>(marker[static_cast<size_t>(c)] - 1)].val +=
vals[static_cast<size_t>(k)];
std::sort(active_rows.begin(), active_rows.end(), [](const auto& lhs, const auto& rhs) {

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.

Do we actually need rows to be sorted?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm afraid we can't avoid it because the original bucket sorting relies on the assumption of sorted indices that we can do prefix sum and nnz entries are implicitly row-ordered by putting them into the ordered buckets 1 to n. Otherwise, we have to use unordered_map for counting.

Comment thread cpp/src/io/mps_parser.cpp Outdated
try {
// Discover active canonical rows and count entries in first-appearance order.
for (size_t k = 0; k < n; ++k) {
const i_t row = std::min(rows[k], cols[k]);

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.

Consider doing a pass at the beginning to put all the entries in the correct triangle. This would reduce the context needed to understand the code and reduce the incidence of max and min in various spots.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a tradeoff between a cleaner code design and avoiding an extra for loop.

Comment thread cpp/src/io/mps_parser.cpp Outdated
* entries that were touched. A workspace must not be shared across concurrent calls.
*/
template <typename i_t, typename f_t>
class coo_canonicalization_workspace_t {

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.

This class is an implementation detail and doesn't really belong in a header file. The use of "friend" below hints at this. Most of the call sites are using canonicalize_quadratic_constraints so they don't need to see coo_canonicalization_workspace_t. Perhaps non-performance-sensitive sites can call a version that allocates a new coo_canonicalization_workspace_t?

Alternatively find a way to make this helper opaque so the body can be moved to a cpp file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The class is exposed due to the existence of append_quadratic_constraint where we add quadratic constraint incrementally while want to keep reusing workspace. Dropping reusing workspace will slow the speed a lot when using add_quadratic_constraint.

I made a new commit trying to keep reusing workspace without exposing the class details in mps_data_model.hpp.

Signed-off-by: YUWEN Chen <yuwchen@nvidia.com>
Signed-off-by: YUWEN Chen <yuwchen@nvidia.com>
Signed-off-by: YUWEN Chen <yuwchen@nvidia.com>
Signed-off-by: YUWEN Chen <yuwchen@nvidia.com>

@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.

Caution

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

⚠️ Outside diff range comments (1)
cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp (1)

275-295: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing @param workspace documentation on append_quadratic_constraint.

The Doxygen block documents linear_values, linear_indices, vals/rows/cols, and constraint_row_type, but not the newly added workspace parameter.

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

📝 Proposed doc fix
    * `@param` constraint_row_type MPS ROWS type: 'L' (<=) or 'G' (>=). Stored as given; 'G' rows are
    *        converted to '<=' form when building the SOCP for the barrier solver. Equality ('E') is
    *        not supported.
+   * `@param` workspace Reusable scratch storage for COO canonicalization; must not be shared across
+   *        concurrent calls.
    */
   void append_quadratic_constraint(i_t constraint_row_index,
🤖 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/io/mps_data_model.hpp` around
lines 275 - 295, The Doxygen comment for append_quadratic_constraint is missing
documentation for the new workspace parameter, so update the existing block to
add an `@param` entry for coo_canonicalization_workspace_t<i_t, f_t>& workspace
and describe its role in canonicalizing the quadratic COO input; keep the rest
of the parameter descriptions consistent with the actual types and behavior of
the 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.

Outside diff comments:
In `@cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp`:
- Around line 275-295: The Doxygen comment for append_quadratic_constraint is
missing documentation for the new workspace parameter, so update the existing
block to add an `@param` entry for coo_canonicalization_workspace_t<i_t, f_t>&
workspace and describe its role in canonicalizing the quadratic COO input; keep
the rest of the parameter descriptions consistent with the actual types and
behavior of the function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0208052b-8614-4a8d-b674-7cbddab7e56e

📥 Commits

Reviewing files that changed from the base of the PR and between fdc9601 and 6decb20.

📒 Files selected for processing (8)
  • cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp
  • cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem.hpp
  • cpp/src/io/lp_parser.cpp
  • cpp/src/io/mps_data_model.cpp
  • cpp/src/io/mps_parser_internal.hpp
  • cpp/src/pdlp/cpu_optimization_problem.cpp
  • cpp/src/pdlp/optimization_problem.cu
🚧 Files skipped from review as they are similar to previous changes (2)
  • cpp/src/io/lp_parser.cpp
  • cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

barrier 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.

2 participants