Skip to content

Update APIs - SOCP support - #1979

Open
Iroy30 wants to merge 82 commits into
NVIDIA:mainfrom
Iroy30:add_socp_support_update_apis
Open

Iroy30 wants to merge 82 commits into
NVIDIA:mainfrom
Iroy30:add_socp_support_update_apis

Conversation

@Iroy30

@Iroy30 Iroy30 commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Description

follows #1941 and #1913
Barrier sequence solves (update_linear_objective and update_rhs) now cover second-order cone programs built from quadratic constraints.
The cache stores the expansion: the pre-expansion sizes, the column permutation, the cone start before and after inequality slacks are inserted, the appended cone-row right-hand sides, and the rows that proved a cone head nonnegative. The crush replays that map, the slack shift, and the free-variable equality substitution so the update lands on the cached barrier problem. The quadratic-constraint constant is kept in the cone-row tail and is not overwritten.
A warm reuse restores the cone block to the initial diagonal before the first factorization. Reuse is refused when cone variables were aliased, and the cone layout must match the cached one.

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

yuwenchen95 and others added 30 commits August 18, 2026 05:29
Signed-off-by: yuwenchen95 <[email protected]>
This PR fixes build after the latest RMM merge broke our pipeline
(rapidsai/rmm@6646d15).

device_scalar no longer accepts a r-value constructor. Replaced with
common constants as inline constexpr that are passed instead of r-value
constants.

<!-- Add brief description here -->

<!-- Add closes #ISSUE_NUMBER here, this would close the issue once PR
is merged, if there is no issue, please feel free to remove this section
-->

- [ ] I am familiar with the [Contributing
Guidelines](https://github.com/NVIDIA/cuopt/blob/HEAD/CONTRIBUTING.md).
- 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
The cache-reuse rebind left one destructor check as settings_. instead of settings_->, which only compiles on CU13 wheels.

Signed-off-by: root <[email protected]>
Keep cache-reuse symbolic_done_ and main's explicit CUstream initialization.

Signed-off-by: Ishika Roy <[email protected]>
Signed-off-by: yuwenchen95 <[email protected]>
…augmented path, and the never-read device_A_x_values snapshot in iteration_data_t; no numerical change.

Signed-off-by: yuwenchen95 <[email protected]>
Crush a new user-space constraint RHS into the cached barrier workspace so a
sequence re-solve can skip convert/presolve/scaling, mirroring
update_linear_objective.

- crush_user_rhs in barrier_transform.hpp negates 'G' rows, checks the rows
  presolve dropped as empty, gathers remaining_constraints and divides by
  row_scales. rhs_shift and rhs_update_supported are recorded on the first
  solve; range rows and folding are refused.
- Empty rows dropped at t=0 are tested against the solve's primal_tol rather
  than exact zero, and an infeasible one short-circuits the next Solve to
  INFEASIBLE without running IPM.
- The single c_dirty flag becomes dirty()/mark_clean() over separate c/b
  flags so further update APIs can reuse the same gate.

Signed-off-by: Ishika Roy <[email protected]>
Recovered from 7d61502; it was deleted by the 698afbe log cleanup. Updated
for the update_rhs naming and the deferred setter cache-invalidation gap.

Signed-off-by: Ishika Roy <[email protected]>
…e_apis

Signed-off-by: Ishika Roy <[email protected]>

# Conflicts:
#	cpp/src/barrier/barrier.cu
#	cpp/src/barrier/device_sparse_matrix.cuh
new_slacks.empty() was far too strict: convert_less_than_to_equal adds a slack
for every inequality row, so any model with an inequality was refused. Only
convert_range_rows destroys the RHS (it zeroes rhs[i] and moves the bounds onto
the slack); artificials leave rhs alone and convert_greater_to_less negates it,
which the crush already mirrors. Gate on num_range_rows instead.

Verified with a QP over a G row: two successive update_rhs re-solves take the
reuse path, skip presolve / reordering / symbolic factorization, and match a
fresh full solve.

Signed-off-by: Ishika Roy <[email protected]>
The repo had no sequence_solve coverage at all. These compare every cached
re-solve against a fresh full solve of the same model, so no assertion depends
on a hand-derived optimum, and each asserts the reuse log line so a test cannot
pass while the gate quietly rejects the model and falls back to a full solve.

Models force the crush paths a one-row QP leaves as no-ops: mixed E/L/G senses,
row norms seven orders of magnitude apart (non-unit row_scales), nonzero
variable lower bounds (rhs_shift of -7; dropping it moves the optimum 115%),
and an empty row presolve drops, covering both the feasible case and the
short-circuit to PrimalInfeasible with no IPM and a surviving cache.

Checked by mutation: removing barrier_presolve_bound_free_variables=0 fails 5
of the 6, all reporting the fallback.

Signed-off-by: Ishika Roy <[email protected]>
…gmented sort, reuse it for a new on-device transpose so the augmented path drops the host A^T build and upload, and cover both with a unit test.

Signed-off-by: yuwenchen95 <[email protected]>
Signed-off-by: yuwenchen95 <[email protected]>

# Conflicts:
#	cpp/src/barrier/device_sparse_matrix.cuh
Comment thread cpp/src/barrier/barrier_cache.cu Outdated
impl_->rhs_dirty = true;
}

template void barrier_cache_t::store_transform<int, double>(

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.

We usually wrap template initialization in an ifdef. Please take a look at some of the other files in the barrier directory for an example

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

updated

std::vector<f_t> const& expanded_objective)
{
std::vector<f_t> problem_objective(static_cast<std::size_t>(problem_num_cols(xf)));
for (i_t j = 0; j < static_cast<i_t>(problem_objective.size()); ++j) {

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: avoid all the static_casts in this loop

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The cast keeps the loop index an i_t while vector::size() is size_t

std::vector<value_t> const& problem_objective)
{
std::vector<value_t> expanded(static_cast<std::size_t>(xf.user_num_cols), value_t(0));
for (i_t j = 0; j < static_cast<i_t>(problem_objective.size()); ++j) {

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: avoid all the static casts in this loop

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The cast keeps the loop index an i_t while vector::size() is size_t

Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
original[static_cast<std::size_t>(i)] =
xf.row_sense[static_cast<std::size_t>(i)] == 'G' ? -b[i] : b[i];
const std::size_t user_num_rows = xf.user_num_rows;
for (std::size_t i = 0; i < user_num_rows; ++i) {

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.

const i_t user_num_rows = xf.user_num_rows;
for (i_t i = 0; i < user_num_rows; ++i)

Comment thread cpp/src/barrier/second_order_cone_kernels.cuh Outdated
Comment thread cpp/src/pdlp/solve.cu Outdated
op_problem.has_quadratic_objective(),
false);
// Must stay in lockstep with the gate in solve_linear_program_with_barrier: this path swaps
// in the slim user_problem_from_transform, so disagreement runs presolve on a fabricated

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.

What is a fabricated problem?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

user_problem_from_transform builds a stand-in problem, not the converted model. The wording is changed now

@Iroy30

Iroy30 commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

/ok to test 2e1a10e

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

CI Test Summary

✅ All 9 test job(s) passed. (4 skipped)

@Iroy30

Iroy30 commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

/ok to test 2f022ba

@yuwenchen95 yuwenchen95 left a comment

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.

Overall is good to me, with some minor comments. Thanks!

bool barrier; // true to use barrier method, false to use dual simplex method
// Equality substitution of zero-cost free variables. A barrier cache cannot refresh the
// stored pivot RHS, so a solve that fills the cache turns this off.
bool barrier_eliminate_free_variables = true;

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: every other field here is initialized in the constructor's init list; could this follow the same pattern (barrier_eliminate_free_variables(true) right after barrier_presolve(false)) with a one-line trailing comment like its neighbors?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

formatted

Comment thread cpp/src/dual_simplex/solve.cpp Outdated
{
lp_status_t status = lp_status_t::UNSET;
simplex_solver_settings_t<i_t, f_t> barrier_settings = settings;
// The cached presolve records cannot be replayed after an RHS update, so the fill solve

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.

It is to disable a presolve step automatically. Can we address it like effective_bound_free_variables?

@Iroy30 Iroy30 Oct 9, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

added effective_eliminate_free_variables like the effective_bound_free_variables

Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
Comment thread cpp/src/dual_simplex/solve.cpp Outdated
impl_->rhs_dirty = true;
}

#ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE

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 really need #ifdef here?

@Iroy30 Iroy30 Oct 9, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Was included to addtress #1979 (comment)

Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
const i_t problem_rows = user_problem.original_num_rows;
std::vector<i_t> row_nz(problem_rows, 0);
for (i_t j = 0; j < user_problem.num_cols; ++j) {
for (i_t p = A.col_start[j]; p < A.col_start[j + 1]; ++p) {

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.

It looks the same as before. Can we undo the change here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It was addressing a previous review
#1979 (comment)

Comment thread cpp/src/barrier/barrier_transform.hpp Outdated
cone_head_bound_t<i_t, f_t> bound;
bound.head_col = head;
// A is CSC, so the head's own column already lists every row it appears in.
for (i_t p = A.col_start[head]; p < A.col_start[head + 1]; ++p) {

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.

The same as above.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

it was addressing a previous review
#1979 (comment)

@Iroy30

Iroy30 commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

/ok to test 6562707

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 P0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants