Skip to content

Implement C APIs for adding quadratic objectives and constraints#1247

Open
rg20 wants to merge 12 commits into
NVIDIA:release/26.06from
rg20:qcqp-c-api
Open

Implement C APIs for adding quadratic objectives and constraints#1247
rg20 wants to merge 12 commits into
NVIDIA:release/26.06from
rg20:qcqp-c-api

Conversation

@rg20
Copy link
Copy Markdown
Contributor

@rg20 rg20 commented May 19, 2026

Description

Adds a composable C API for building QP and QCQP models without growing the number of monolithic cuOptCreate* functions. Users create the linear problem with cuOptCreateProblem or cuOptCreateRangedProblem, then attach quadratic objectives and constraints incrementally.

Marks cuOptCreateQuadraticProblem and cuOptCreateQuadraticRangedProblem as deprecated (runtime warnings + docs) and migrates existing C API QP tests to the new workflow.

New APIs

  • cuOptAddQuadraticObjective Adds xT Q x using coordinate (triplet) format (row_index, col_index, coeff).
  • cuOptAddQuadraticConstraint Appends one constraint xT Q x + dT x <= / >= rhs

Triplet input is converted to CSR internally before calling the optimization problem interface.

Deprecation
cuOptCreateQuadraticProblem and cuOptCreateQuadraticRangedProblem log CUOPT_LOG_WARN on use with migration guidance

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

@rg20 rg20 requested review from a team as code owners May 19, 2026 20:05
@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented May 19, 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.

@rg20 rg20 added this to the 26.06 milestone May 19, 2026
@rg20 rg20 requested review from chris-maes and removed request for nguidotti May 19, 2026 20:05
@rg20 rg20 added non-breaking Introduces a non-breaking change feature request New feature or request labels May 19, 2026
@rg20
Copy link
Copy Markdown
Contributor Author

rg20 commented May 19, 2026

/ok to test 7a67568

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

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
📝 Walkthrough

Walkthrough

Adds C APIs cuOptSetQuadraticObjective and cuOptAddQuadraticConstraint to attach quadratic objective triplets and QCQP quadratic constraints to existing linear/ranged problems, deprecates legacy quadratic constructors, implements CSR assembly helpers, and updates tests and docs to the incremental workflow.

Changes

Incremental quadratic objective and constraint addition

Layer / File(s) Summary
API contract and public documentation
cpp/include/cuopt/linear_programming/cuopt_c.h, docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst
Header docs now state the optimization object covers LP/MIP/QP/QCQP, deprecate cuOptCreateQuadraticProblem/cuOptCreateQuadraticRangedProblem, and declare cuOptSetQuadraticObjective and cuOptAddQuadraticConstraint accepting coordinate-format triplet arrays.
Implementation infrastructure and helper utilities
cpp/src/pdlp/cuopt_c.cpp
Adds <algorithm> and anonymous-namespace helpers to sort/validate/merge triplets into per-row CSR and defines deprecation warning strings used by legacy constructors.
Quadratic objective and constraint addition implementation
cpp/src/pdlp/cuopt_c.cpp
Implements cuOptSetQuadraticObjective (input validation, triplet->CSR conversion, replace stored quadratic objective) and cuOptAddQuadraticConstraint (validate quad and linear parts, triplet->CSR conversion, append new quadratic constraint with linear terms and metadata).
Core interface and backend implementations
cpp/include/cuopt/linear_programming/optimization_problem_interface.hpp, cpp/include/cuopt/linear_programming/optimization_problem.hpp, cpp/include/cuopt/linear_programming/cpu_optimization_problem.hpp, cpp/src/pdlp/cpu_optimization_problem.cpp, cpp/src/pdlp/optimization_problem.cu
Adds add_quadratic_constraint to the public interface and implements overrides that validate CSR-like buffers and append a single quadratic constraint record (CSR quad matrix + optional sparse linear term + RHS/sense).
Test refactoring to new incremental pattern
cpp/tests/linear_programming/c_api_tests/c_api_test.c
Refactors test_quadratic_problem and test_quadratic_ranged_problem to provide quadratic triplet arrays and use cuOptCreateProblem/cuOptCreateRangedProblem followed by cuOptSetQuadraticObjective.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

improvement

Suggested reviewers

  • yuwenchen95
  • rgsl888prabhu
  • mlubin
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% 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 'Implement C APIs for adding quadratic objectives and constraints' accurately summarizes the primary change—adding two new composable C API functions for building QP/QCQP models incrementally.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the motivation (composable APIs), the new functions added, deprecation details, and migration guidance.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (1)
cpp/tests/linear_programming/c_api_tests/c_api_test.c (1)

1206-1392: ⚡ Quick win

Consider adding edge case tests for the new incremental quadratic API.

The migrated tests cover the basic happy path but the new incremental workflow introduces edge cases that should be validated:

  • Multiple calls: Calling cuOptAddQuadraticObjective multiple times on the same problem (accumulation behavior)
  • Duplicate triplets: Same (row, col) appearing multiple times in a single call
  • Empty entries: Calling with num_entries=0
  • Off-diagonal terms: Quadratic terms like x1*x2
  • QCQP constraints: cuOptAddQuadraticConstraint has no test coverage

As per coding guidelines, tests should "ensure coverage hits edge cases introduced by the new flow (e.g., multiple calls, duplicate (row,col) triplets, empty entries, and correct QCQP constraint 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/tests/linear_programming/c_api_tests/c_api_test.c` around lines 1206 -
1392, The tests only cover the basic quadratic happy path; add new test cases
(new functions or extending
test_quadratic_problem/test_quadratic_ranged_problem) that exercise the
incremental quadratic API edge cases: call cuOptAddQuadraticObjective multiple
times on the same problem to verify accumulation behavior, submit duplicate
(row,col) triplets in a single cuOptAddQuadraticObjective call to confirm they
are summed/handled, call cuOptAddQuadraticObjective with num_entries=0 to ensure
it is a no-op and returns success, include off-diagonal terms (e.g., Q entries
for (0,1) and (1,0)) and verify solution/objective, and add tests for
cuOptAddQuadraticConstraint (QCQP) using
cuOptCreateProblem/cuOptCreateRangedProblem then cuOptSolve to check termination
status and objective; in each test assert the returned status is CUOPT_SUCCESS,
validate expected objective/termination values, and always call
cuOptDestroyProblem/cuOptDestroySolverSettings/cuOptDestroySolution for cleanup.
🤖 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.

Inline comments:
In `@cpp/include/cuopt/linear_programming/cuopt_c.h`:
- Around line 436-473: The PR adds new C ABI entry points
(cuOptAddQuadraticObjective and cuOptAddQuadraticConstraint) which expand the
public ABI surface; update the maintainer/action items by (1) confirming this
ABI change is intentional with the release manager, (2) adding an entry to the
project’s ABI/compatibility changelog and migration notes documenting these new
functions and their expected stability, and (3) tagging these declarations in
cuopt_c.h (or surrounding documentation) with an explicit note that they are
stable C ABI exports so reviewers know this was deliberate; reference the new
symbols cuOptAddQuadraticObjective and cuOptAddQuadraticConstraint when making
the changelog/migration entry.

In `@cpp/src/pdlp/cuopt_c.cpp`:
- Around line 539-563: Add a catch for std::exception in both extern "C" entry
points to prevent C++ exceptions escaping the C API: inside
cuOptAddQuadraticObjective (currently catching raft::exception) and likewise
inside cuOptAddQuadraticConstraint, append a catch(const std::exception& e) {
return CUOPT_INVALID_ARGUMENT; } (or equivalent) after the existing
raft::exception catch so any std::bad_alloc or other std::exception-derived
errors are handled and the function returns CUOPT_INVALID_ARGUMENT instead of
letting exceptions propagate.

In `@docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst`:
- Around line 54-58: Remove the inline deprecation note that mentions
cuOptCreateQuadraticProblem and cuOptCreateQuadraticRangedProblem from this rst
page and replace it with a short pointer to the centralized external docs policy
page; specifically, delete the block referencing cuOptCreateQuadraticProblem and
cuOptCreateQuadraticRangedProblem and instead add a single-sentence link or
reference directing readers to the external migration/deprecation guidance for
using cuOptCreateProblem / cuOptCreateRangedProblem and the recommended
cuOptAddQuadraticObjective (and cuOptAddQuadraticConstraint for QCQP) workflow.

---

Nitpick comments:
In `@cpp/tests/linear_programming/c_api_tests/c_api_test.c`:
- Around line 1206-1392: The tests only cover the basic quadratic happy path;
add new test cases (new functions or extending
test_quadratic_problem/test_quadratic_ranged_problem) that exercise the
incremental quadratic API edge cases: call cuOptAddQuadraticObjective multiple
times on the same problem to verify accumulation behavior, submit duplicate
(row,col) triplets in a single cuOptAddQuadraticObjective call to confirm they
are summed/handled, call cuOptAddQuadraticObjective with num_entries=0 to ensure
it is a no-op and returns success, include off-diagonal terms (e.g., Q entries
for (0,1) and (1,0)) and verify solution/objective, and add tests for
cuOptAddQuadraticConstraint (QCQP) using
cuOptCreateProblem/cuOptCreateRangedProblem then cuOptSolve to check termination
status and objective; in each test assert the returned status is CUOPT_SUCCESS,
validate expected objective/termination values, and always call
cuOptDestroyProblem/cuOptDestroySolverSettings/cuOptDestroySolution for cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0452693c-7da8-4fd0-8824-7e13f30eb554

📥 Commits

Reviewing files that changed from the base of the PR and between b145cc3 and 7a67568.

📒 Files selected for processing (4)
  • cpp/include/cuopt/linear_programming/cuopt_c.h
  • cpp/src/pdlp/cuopt_c.cpp
  • cpp/tests/linear_programming/c_api_tests/c_api_test.c
  • docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst

Comment thread cpp/include/cuopt/linear_programming/cuopt_c.h Outdated
Comment thread cpp/src/pdlp/cuopt_c.cpp
Comment on lines +54 to +58
.. note::
``cuOptCreateQuadraticProblem`` and ``cuOptCreateQuadraticRangedProblem`` are deprecated.
Prefer ``cuOptCreateProblem`` or ``cuOptCreateRangedProblem`` followed by
``cuOptAddQuadraticObjective`` (and ``cuOptAddQuadraticConstraint`` for QCQP).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Move this deprecation/migration note to external docs policy location.

Please avoid carrying deprecation/migration guidance inline here; keep this page concise and point to the external docs location instead.

Based on learnings: for RAPIDS/cuOpt, deprecation notices and migration guidance should live on the external docs site, not in-repo .rst docs.

🤖 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 `@docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst` around lines 54 -
58, Remove the inline deprecation note that mentions cuOptCreateQuadraticProblem
and cuOptCreateQuadraticRangedProblem from this rst page and replace it with a
short pointer to the centralized external docs policy page; specifically, delete
the block referencing cuOptCreateQuadraticProblem and
cuOptCreateQuadraticRangedProblem and instead add a single-sentence link or
reference directing readers to the external migration/deprecation guidance for
using cuOptCreateProblem / cuOptCreateRangedProblem and the recommended
cuOptAddQuadraticObjective (and cuOptAddQuadraticConstraint for QCQP) workflow.

Comment thread cpp/src/pdlp/cuopt_c.cpp Outdated

for (cuopt_int_t row = 0; row < num_rows; ++row) {
auto& entries = row_entries[row];
std::sort(entries.begin(), entries.end());
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 need to sort here? That can be expensive. I'm not sure we require the entries to be sorted at this point.

Comment thread cpp/src/pdlp/cuopt_c.cpp
Comment thread cpp/src/pdlp/cuopt_c.cpp Outdated
std::vector<cuopt_int_t>& indices,
std::vector<cuopt_float_t>& values)
{
std::map<triplet_key_t, cuopt_float_t, triplet_key_cmp_t> triplets;
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.

I think this can be done more efficiently using an algorithm similar to the one below from:
https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/dev/CSparse/Source/cs_compress.c

We can allocate a workspace w of size n inside cuOptOptimizationProblem to avoid the overhead of creating it each time.

cs *cs_compress (const cs *T)
{
    csi m, n, nz, p, k, *Cp, *Ci, *w, *Ti, *Tj ;
    double *Cx, *Tx ;
    cs *C ;
    if (!CS_TRIPLET (T)) return (NULL) ;                /* check inputs */
    m = T->m ; n = T->n ; Ti = T->i ; Tj = T->p ; Tx = T->x ; nz = T->nz ;
    C = cs_spalloc (m, n, nz, Tx != NULL, 0) ;          /* allocate result */
    w = cs_calloc (n, sizeof (csi)) ;                   /* get workspace */
    if (!C || !w) return (cs_done (C, w, NULL, 0)) ;    /* out of memory */
    Cp = C->p ; Ci = C->i ; Cx = C->x ;
    for (k = 0 ; k < nz ; k++) w [Tj [k]]++ ;           /* column counts */
    cs_cumsum (Cp, w, n) ;                              /* column pointers */
    for (k = 0 ; k < nz ; k++)
    {
        Ci [p = w [Tj [k]]++] = Ti [k] ;    /* A(i,j) is the pth entry in C */
        if (Cx) Cx [p] = Tx [k] ;
    }
    return (cs_done (C, w, NULL, 1)) ;      /* success; free w and return C */
}

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.

Changed the implementation now

@anandhkb anandhkb added the P0 label May 20, 2026
Copy link
Copy Markdown

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

Caution

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

⚠️ Outside diff range comments (3)
cpp/tests/linear_programming/c_api_tests/c_api_test.c (1)

1337-1375: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Exercise non-trivial triplet assembly in one of these quadratic tests.

Both cases use already-sorted, duplicate-free diagonal triplets, so a bug in the new triplet-to-CSR merge/sort path would still pass. Please make at least one test use out-of-order triplets and/or duplicate (row, col) entries that collapse to the same Q, so the expected optimum stays unchanged while the conversion logic is actually exercised.

♻️ Example tweak
-  cuopt_int_t Q_row_index[] = {0, 1};
-  cuopt_int_t Q_col_index[] = {0, 1};
-  cuopt_float_t Q_coeff[]   = {1.0, 4.0};
+  cuopt_int_t Q_row_index[] = {1, 0, 1};
+  cuopt_int_t Q_col_index[] = {1, 0, 1};
+  cuopt_float_t Q_coeff[]   = {1.5, 1.0, 2.5};  // duplicate (1,1), unsorted rows
...
-  status = cuOptAddQuadraticObjective(problem, 2, Q_row_index, Q_col_index, Q_coeff);
+  status = cuOptAddQuadraticObjective(problem, 3, Q_row_index, Q_col_index, Q_coeff);

As per coding guidelines, cpp/tests/**: "Numerical correctness validation (not just "runs without error")" and "When a bug fix lands, a regression test should cover the specific case."

Also applies to: 1431-1467

🤖 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/c_api_tests/c_api_test.c` around lines 1337 -
1375, Current quadratic test uses already-sorted, unique diagonal triplets so
the triplet-to-CSR merge/sort path isn't exercised; modify the test data passed
to cuOptAddQuadraticObjective (Q_row_index, Q_col_index, Q_coeff) to include
out-of-order entries and at least one duplicated (row,col) pair whose
coefficients sum to the same final Q value (e.g., split a diagonal coefficient
into two entries and place them non-sequentially), leaving the rest of the
problem identical so the expected optimum remains unchanged; ensure the
duplicated entries collapse correctly by keeping the same expected solution and
verifying numerical correctness after calling cuOptAddQuadraticObjective.
cpp/src/pdlp/cuopt_c.cpp (2)

267-278: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Reject negative dimensions and stop narrowing cuopt_int_t to int.

Both constructors still use num_constraints/num_variables as array indices and vector sizes before proving they are non-negative, and the new validation/population loops narrow num_variables to int. On 64-bit builds this can either walk past INT_MAX incorrectly or, for negative counts, hit OOB reads / huge signed-to-size_t allocations instead of returning CUOPT_INVALID_ARGUMENT.

As per coding guidelines: Flag missing input validation at library and server boundaries.

Also applies to: 300-303, 331-344, 367-376

🤖 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/cuopt_c.cpp` around lines 267 - 278, The code must validate
num_variables and num_constraints are non-negative before using them as
indices/sizes and must avoid narrowing cuopt_int_t to int in loops; add explicit
checks that num_variables >= 0 and num_constraints >= 0 at the start of the
constructor/validation functions (before any array accesses or vector
allocations) and return CUOPT_INVALID_ARGUMENT if not, then change loops that
currently use "for (int j = 0; j < num_variables; j++)" to use the original
cuopt_int_t (or an unsigned size_t derived only after a signed non-negative
check) when indexing variable_types and calling
detail::is_valid_public_var_type_code, and apply the same pattern to the other
mentioned ranges (the blocks around lines 300-303, 331-344, 367-376) so no
signed-to-int narrowing or out-of-bounds accesses can occur.

625-644: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid copying all existing quadratic constraints on every append.

cuOptAddQuadraticConstraint copies the entire existing quadratic-constraint vector, adds one entry, then writes the whole thing back. Building N quadratic constraints this way turns model construction into repeated deep copies of all prior Q data, which is exactly the bad case for an incremental QCQP API.

As per coding guidelines: Flag excessive allocations in hot paths; prefer avoiding O(n²) construction patterns when a scalable append path is available.

🧹 Nitpick comments (1)
cpp/include/cuopt/linear_programming/cuopt_c.h (1)

443-458: ⚡ Quick win

Document duplicate quadratic-triplet handling here too.

cuOptAddQuadraticConstraint goes through the same COO accumulation path as cuOptAddQuadraticObjective, so repeated (row_index, col_index) pairs are summed before storage. Please state that in this docblock as well; right now the public contract is less precise than the implementation.

As per coding guidelines: Verify parameter descriptions match actual types/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/linear_programming/cuopt_c.h` around lines 443 - 458,
Update the cuOptAddQuadraticConstraint docblock to state that quadratic matrix
entries provided in triplet/COO form (row_index, col_index, coeff) are
accumulated/summed for duplicate (row_index, col_index) pairs before storage
(same behavior as cuOptAddQuadraticObjective), and confirm the parameter
descriptions reflect actual types and semantics used by the implementation
(e.g., 0-based indices, lengths are quad_num_entries and num_lin_entries, and
linear term uses parallel arrays linear_index/linear_coeff); adjust wording to
match the implementation’s accumulation behavior and indexing conventions so the
public contract is precise.
🤖 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/src/pdlp/cuopt_c.cpp`:
- Around line 267-278: The code must validate num_variables and num_constraints
are non-negative before using them as indices/sizes and must avoid narrowing
cuopt_int_t to int in loops; add explicit checks that num_variables >= 0 and
num_constraints >= 0 at the start of the constructor/validation functions
(before any array accesses or vector allocations) and return
CUOPT_INVALID_ARGUMENT if not, then change loops that currently use "for (int j
= 0; j < num_variables; j++)" to use the original cuopt_int_t (or an unsigned
size_t derived only after a signed non-negative check) when indexing
variable_types and calling detail::is_valid_public_var_type_code, and apply the
same pattern to the other mentioned ranges (the blocks around lines 300-303,
331-344, 367-376) so no signed-to-int narrowing or out-of-bounds accesses can
occur.

In `@cpp/tests/linear_programming/c_api_tests/c_api_test.c`:
- Around line 1337-1375: Current quadratic test uses already-sorted, unique
diagonal triplets so the triplet-to-CSR merge/sort path isn't exercised; modify
the test data passed to cuOptAddQuadraticObjective (Q_row_index, Q_col_index,
Q_coeff) to include out-of-order entries and at least one duplicated (row,col)
pair whose coefficients sum to the same final Q value (e.g., split a diagonal
coefficient into two entries and place them non-sequentially), leaving the rest
of the problem identical so the expected optimum remains unchanged; ensure the
duplicated entries collapse correctly by keeping the same expected solution and
verifying numerical correctness after calling cuOptAddQuadraticObjective.

---

Nitpick comments:
In `@cpp/include/cuopt/linear_programming/cuopt_c.h`:
- Around line 443-458: Update the cuOptAddQuadraticConstraint docblock to state
that quadratic matrix entries provided in triplet/COO form (row_index,
col_index, coeff) are accumulated/summed for duplicate (row_index, col_index)
pairs before storage (same behavior as cuOptAddQuadraticObjective), and confirm
the parameter descriptions reflect actual types and semantics used by the
implementation (e.g., 0-based indices, lengths are quad_num_entries and
num_lin_entries, and linear term uses parallel arrays
linear_index/linear_coeff); adjust wording to match the implementation’s
accumulation behavior and indexing conventions so the public contract is
precise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3d07538f-7159-49e1-8b16-79c0a2ee5e4f

📥 Commits

Reviewing files that changed from the base of the PR and between 7a67568 and 6e5175e.

📒 Files selected for processing (4)
  • cpp/include/cuopt/linear_programming/cuopt_c.h
  • cpp/src/pdlp/cuopt_c.cpp
  • cpp/tests/linear_programming/c_api_tests/c_api_test.c
  • docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst
✅ Files skipped from review due to trivial changes (1)
  • docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst


/** @brief Create an optimization problem of the form
*
* @deprecated Use ``cuOptCreateProblem`` to set up the linear problem, then
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.

Are we allowed to use [[deprecated]]? It's a C23 feature.

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 customer facing API. Not sure if we want to add this restriction on the version of C.

Comment thread cpp/src/pdlp/cuopt_c.cpp Outdated
using quadratic_constraint_t =
optimization_problem_interface_t<cuopt_int_t, cuopt_float_t>::quadratic_constraint_t;

std::vector<quadratic_constraint_t> constraints(op_problem->get_quadratic_constraints());
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.

I think this is making a copy of all the quadratic constraints each time we add a new constraint.

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.

You are right, I will implement a new C++ API for adding constraints

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.

Fixed it

@mlubin
Copy link
Copy Markdown
Contributor

mlubin commented May 20, 2026

We should have test coverage for cuOptAddQuadraticConstraint including for unsupported constraint types like quadratic >= anything or general quadratic.

Copy link
Copy Markdown

@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

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

Inline comments:
In `@cpp/src/pdlp/cpu_optimization_problem.cpp`:
- Around line 189-193: The vector::assign calls on qc
(quadratic_values/quadratic_indices/quadratic_offsets/linear_values/linear_indices)
perform ptr and ptr+size pointer arithmetic even when the incoming pointer may
be nullptr for a zero size; guard each assignment with a size check (e.g., if
size_* > 0) and only call assign(ptr, ptr + size) in that case, otherwise clear
or assign an empty range to the corresponding qc container to avoid undefined
behavior from null-pointer arithmetic.

In `@cpp/src/pdlp/optimization_problem.cu`:
- Around line 261-265: The five unconditional vector assigns
(qc.quadratic_values.assign(quadratic_values, quadratic_values +
size_quadratic_values), qc.quadratic_indices.assign(...),
qc.quadratic_offsets.assign(...), qc.linear_values.assign(...),
qc.linear_indices.assign(...)) may pass null pointers when their corresponding
size_* is zero; guard each assign with an if (size_X > 0) check (e.g., if
(size_quadratic_values > 0) { qc.quadratic_values.assign(quadratic_values,
quadratic_values + size_quadratic_values); }) matching the existing pattern used
elsewhere so assign() is only called with valid non-null ranges.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f1a45c67-dc86-4fd7-8fc5-bae4fd1f6c89

📥 Commits

Reviewing files that changed from the base of the PR and between 65f4199 and f56d655.

📒 Files selected for processing (6)
  • cpp/include/cuopt/linear_programming/cpu_optimization_problem.hpp
  • cpp/include/cuopt/linear_programming/optimization_problem.hpp
  • cpp/include/cuopt/linear_programming/optimization_problem_interface.hpp
  • cpp/src/pdlp/cpu_optimization_problem.cpp
  • cpp/src/pdlp/cuopt_c.cpp
  • cpp/src/pdlp/optimization_problem.cu

Comment thread cpp/src/pdlp/cpu_optimization_problem.cpp
Comment thread cpp/src/pdlp/optimization_problem.cu
@rgsl888prabhu rgsl888prabhu changed the base branch from main to release/26.06 May 20, 2026 17:27
@chris-maes
Copy link
Copy Markdown
Contributor

We should have test coverage for cuOptAddQuadraticConstraint including for unsupported constraint types like quadratic >= anything or general quadratic.

We support >= for quadratic constraints. We should also support general convex quadratic constraints.

Comment thread cpp/src/pdlp/cuopt_c.cpp Outdated
if (row_a != row_b) { return row_a < row_b; }
return col_index[a] < col_index[b];
};
std::sort(perm.begin(), perm.end(), triplet_less);
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 is O(nnz log nnz). That can be quite expensive. You can do a linear bucket sort here.

Copy link
Copy Markdown
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.

Let's get the complexity of COO to CSR down to O(n + nnz) before merging.

@rg20
Copy link
Copy Markdown
Contributor Author

rg20 commented May 21, 2026

We should have test coverage for cuOptAddQuadraticConstraint including for unsupported constraint types like quadratic >= anything or general quadratic.

We support >= for quadratic constraints. We should also support general convex quadratic constraints.

Can the barrier handle >= for quadratic constraints? The constraints become non-convex right?

Comment thread cpp/src/pdlp/cuopt_c.cpp Outdated
perm[static_cast<size_t>(row_cursor[row]++)] = k;
}

// Per row: sort by column, merge duplicates, emit CSR. Row grouping is O(nnz + num_rows);
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 need sorted columns in the CSR downstream?

If not, let's skip the sort. If so, the most efficient sort is to do a double transpose. Convert the CSR to CSC and then convert back to CSR.

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.

If we don't sort, then we can't handle the duplicates.

Comment thread cpp/src/pdlp/cuopt_c.cpp Outdated
std::sort(row_begin, row_end, col_less);

cuopt_int_t last_col = -1;
for (auto it = row_begin; it != row_end; ++it) {
Copy link
Copy Markdown
Contributor

@chris-maes chris-maes May 21, 2026

Choose a reason for hiding this comment

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

I think this could be simplified to just

cuopt_int_t last_col = -1
for (cuopt_int idx = start; idx < end; idx++) {
     const cuopt_int_t col = col_index[idx];
     // ...

Comment thread cpp/src/pdlp/cuopt_c.cpp Outdated
for (auto it = row_begin; it != row_end; ++it) {
const cuopt_int_t idx = *it;
const cuopt_int_t col = col_index[idx];
if (it != row_begin && col == last_col) {
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.

if (idx != start && ...

Comment thread cpp/src/pdlp/cuopt_c.cpp Outdated

auto row_begin = perm.begin() + static_cast<ptrdiff_t>(start);
auto row_end = perm.begin() + static_cast<ptrdiff_t>(end);
std::sort(row_begin, row_end, col_less);
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.

If you can avoid the sort, you can allocate a dense array of size n, then when looping over columns in a row, mark the column in the dense array. This allows you to detect duplicates. If you mark with the row number you do not need to clear the mark.

@rg20
Copy link
Copy Markdown
Contributor Author

rg20 commented May 22, 2026

/ok to test 66626a3

@rg20
Copy link
Copy Markdown
Contributor Author

rg20 commented May 22, 2026

/ok to test fdff245

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

Labels

feature request New feature or request non-breaking Introduces a non-breaking change P0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants