Fix a bug in dual variables and reduced costs when we add implied bounds on free variables#1133
Fix a bug in dual variables and reduced costs when we add implied bounds on free variables#1133chris-maes wants to merge 38 commits into
Conversation
…nds on free variables
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (258)
📝 Walkthrough<review_stack_artifact> ✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
cpp/src/dual_simplex/presolve.cpp (1)
1754-1769: Avoid scanning the full matrix for every corrected variable.The nested column scan makes this postsolve step scale with the whole matrix for each bounded free variable. Converting
original_problem.Ato CSR once and iterating onlybfv.constraintkeeps the same math but avoids a large postsolve slowdown on wide problems.♻️ Refactor sketch
if (!presolve_info.bounded_free_variables.empty()) { settings.log.printf("Post-solve: Correcting duals for %d bounded free variables\n", static_cast<i_t>(presolve_info.bounded_free_variables.size())); const csc_matrix_t<i_t, f_t>& A = original_problem.A; + csr_matrix_t<i_t, f_t> Arow(0, 0, 0); + A.to_compressed_row(Arow); for (const auto& bfv : presolve_info.bounded_free_variables) { const f_t w_j = input_z[bfv.variable]; if (w_j == 0.0) { continue; } const f_t du = w_j / bfv.coefficient; input_y[bfv.constraint] += du; - for (i_t j = 0; j < A.n; j++) { - const i_t col_start = A.col_start[j]; - const i_t col_end = A.col_start[j + 1]; - for (i_t p = col_start; p < col_end; p++) { - if (A.i[p] == bfv.constraint) { - input_z[j] -= A.x[p] * du; - break; - } - } + const i_t row_start = Arow.row_start[bfv.constraint]; + const i_t row_end = Arow.row_start[bfv.constraint + 1]; + for (i_t p = row_start; p < row_end; ++p) { + input_z[Arow.j[p]] -= Arow.x[p] * du; } } }As per coding guidelines, "Assess algorithmic complexity for large-scale problems (millions of variables/constraints); ensure O(n log n) or better, not O(n²) or worse."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cpp/src/dual_simplex/presolve.cpp` around lines 1754 - 1769, The loop currently scans every column of original_problem.A (CSC) for each presolve_info.bounded_free_variables entry which is O(n²); convert or provide a CSR view of original_problem.A once (e.g., build a row-oriented structure like A_csr or a helper method getRow(constraint)) and then, inside the bounded_free_variables loop (referencing input_z, input_y, bfv.constraint, bfv.coefficient), use the CSR row for bfv.constraint to update input_z[j] directly for only the nonzeros in that row and avoid the inner column scan; ensure the CSR conversion is done once before the loop and reuse it for all bfv entries to preserve numerical results while reducing complexity.cpp/tests/dual_simplex/unit_tests/solve_barrier.cu (1)
184-217: Add the upper-bound / negated-variable polarity as a sibling case.This only covers a formerly-free variable that ends up with a retained lower implied bound. The new code also has separate upper-bound bookkeeping and then routes those variables through the
negated_variablesround-trip incpp/src/dual_simplex/presolve.cpp, so that branch is still untested here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cpp/tests/dual_simplex/unit_tests/solve_barrier.cu` around lines 184 - 217, The test only exercises the case where a formerly-free variable receives a retained lower implied bound; add a sibling test that triggers the upper-bound/negated-variable path so presolve's upper-bound bookkeeping and the negated_variables round-trip in cpp/src/dual_simplex/presolve.cpp are exercised. Copy the existing TEST(barrier, min_x_squared_free_variable_dual_correction) logic into a new TEST (e.g., min_x_squared_free_variable_negated_polarity), use a problem variant (or modify the MPS) that forces an implied upper bound on the free variable (so solve_lp and parse_mps are called with that MPS), call cuopt::linear_programming::solve_lp and the same host_copy/EXPECT checks but assert the expected primal/dual/reduced-cost values for the negated-variable outcome, and ensure the new test references the same setup helpers (get_rapids_dataset_root_dir, parse_mps, solve_lp) so the negated_variables branch is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cpp/src/dual_simplex/presolve.cpp`:
- Around line 1751-1759: The loop updating input_y for
presolve_info.bounded_free_variables divides by bfv.coefficient and only skips
when w_j == 0.0, which is numerically unsafe; change the guard to check for
near-zero values using an epsilon (e.g., fabs(bfv.coefficient) < eps or
fabs(w_j) < eps) and skip or clamp the update when either the coefficient or w_j
is too small to avoid huge du; update the conditional around computing du
(referencing presolve_info.bounded_free_variables, bfv.coefficient, input_z,
input_y) to use an absolute epsilon comparison and optionally log or count
skipped tiny-coefficient cases instead of performing the division.
---
Nitpick comments:
In `@cpp/src/dual_simplex/presolve.cpp`:
- Around line 1754-1769: The loop currently scans every column of
original_problem.A (CSC) for each presolve_info.bounded_free_variables entry
which is O(n²); convert or provide a CSR view of original_problem.A once (e.g.,
build a row-oriented structure like A_csr or a helper method getRow(constraint))
and then, inside the bounded_free_variables loop (referencing input_z, input_y,
bfv.constraint, bfv.coefficient), use the CSR row for bfv.constraint to update
input_z[j] directly for only the nonzeros in that row and avoid the inner column
scan; ensure the CSR conversion is done once before the loop and reuse it for
all bfv entries to preserve numerical results while reducing complexity.
In `@cpp/tests/dual_simplex/unit_tests/solve_barrier.cu`:
- Around line 184-217: The test only exercises the case where a formerly-free
variable receives a retained lower implied bound; add a sibling test that
triggers the upper-bound/negated-variable path so presolve's upper-bound
bookkeeping and the negated_variables round-trip in
cpp/src/dual_simplex/presolve.cpp are exercised. Copy the existing TEST(barrier,
min_x_squared_free_variable_dual_correction) logic into a new TEST (e.g.,
min_x_squared_free_variable_negated_polarity), use a problem variant (or modify
the MPS) that forces an implied upper bound on the free variable (so solve_lp
and parse_mps are called with that MPS), call
cuopt::linear_programming::solve_lp and the same host_copy/EXPECT checks but
assert the expected primal/dual/reduced-cost values for the negated-variable
outcome, and ensure the new test references the same setup helpers
(get_rapids_dataset_root_dir, parse_mps, solve_lp) so the negated_variables
branch is covered.
🪄 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: Pro Plus
Run ID: c207bc0d-0074-4238-af72-79a21036cba5
📒 Files selected for processing (6)
cpp/src/dual_simplex/presolve.cppcpp/src/dual_simplex/presolve.hppcpp/src/dual_simplex/solve.cppcpp/tests/dual_simplex/unit_tests/solve.cppcpp/tests/dual_simplex/unit_tests/solve_barrier.cudatasets/quadratic_programming/min_x_squared.mps
| if (!presolve_info.bounded_free_variables.empty()) { | ||
| settings.log.printf("Post-solve: Correcting duals for %d bounded free variables\n", | ||
| static_cast<i_t>(presolve_info.bounded_free_variables.size())); | ||
| const csc_matrix_t<i_t, f_t>& A = original_problem.A; | ||
| for (const auto& bfv : presolve_info.bounded_free_variables) { | ||
| const f_t w_j = input_z[bfv.variable]; | ||
| if (w_j == 0.0) { continue; } | ||
| const f_t du = w_j / bfv.coefficient; | ||
| input_y[bfv.constraint] += du; |
There was a problem hiding this comment.
Guard the dual update against tiny a_{i*,j} and tiny w_j.
This divides by bfv.coefficient directly and only skips exact-zero w_j. On ill-conditioned rows, a near-zero coefficient will blow up du, and exact floating-point equality here is too strict for postsolve residuals.
As per coding guidelines, "Check numerical stability: prevent overflow/underflow, precision loss, division by zero/near-zero, and use epsilon comparisons for floating-point equality checks."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cpp/src/dual_simplex/presolve.cpp` around lines 1751 - 1759, The loop
updating input_y for presolve_info.bounded_free_variables divides by
bfv.coefficient and only skips when w_j == 0.0, which is numerically unsafe;
change the guard to check for near-zero values using an epsilon (e.g.,
fabs(bfv.coefficient) < eps or fabs(w_j) < eps) and skip or clamp the update
when either the coefficient or w_j is too small to avoid huge du; update the
conditional around computing du (referencing
presolve_info.bounded_free_variables, bfv.coefficient, input_z, input_y) to use
an absolute epsilon comparison and optionally log or count skipped
tiny-coefficient cases instead of performing the division.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
We remove small coefficients in A in presolve. I'm not sure if this is necessary.
|
closes #1119 |
|
/ok to test c29cc5e |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
cpp/src/dual_simplex/presolve.hpp (1)
132-139: Clarify index-space/sign semantics inbounded_free_var_tdocs.Please document whether
variable/constraintare indices in the original LP space and whethercoefficientis stored before or after any presolve sign transformations. That contract is easy to misread later and is central to the dual correction logic.Suggested doc tweak
struct bounded_free_var_t { - i_t variable; // j: the originally-free variable - i_t constraint; // i*: the constraint that implied the bound - f_t coefficient; // a_{i*,j}: the coefficient of x_j in constraint i* + i_t variable; // j in original problem indexing: originally-free variable + i_t constraint; // i* in original problem indexing: constraint that implied the bound + f_t coefficient; // a_{i*,j} used by dual correction (document transformed/original sign convention) };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cpp/src/dual_simplex/presolve.hpp` around lines 132 - 139, Clarify the contract for bounded_free_var_t by updating its doc comment: state that variable and constraint are indices in the original (pre-presolve) LP index space (i.e., they refer to original variable j and original constraint i*), and specify that coefficient stores the original matrix value a_{i*,j} prior to any sign or scaling transformations performed during presolve so uncrush/dual correction can apply the same presolve sign logic; reference bounded_free_var_t and its fields variable, constraint, and coefficient in the comment so future readers know these are original indices and the coefficient is the pre-presolve value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@cpp/src/dual_simplex/presolve.hpp`:
- Around line 132-139: Clarify the contract for bounded_free_var_t by updating
its doc comment: state that variable and constraint are indices in the original
(pre-presolve) LP index space (i.e., they refer to original variable j and
original constraint i*), and specify that coefficient stores the original matrix
value a_{i*,j} prior to any sign or scaling transformations performed during
presolve so uncrush/dual correction can apply the same presolve sign logic;
reference bounded_free_var_t and its fields variable, constraint, and
coefficient in the comment so future readers know these are original indices and
the coefficient is the pre-presolve value.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: efb61bb2-7b75-4b86-8b3d-f11088198164
📒 Files selected for processing (1)
cpp/src/dual_simplex/presolve.hpp
|
/ok to test 3ee78ee |
|
🔔 Hi @anandhkb @chris-maes, this pull request has had no activity for 7 days. Please update or let us know if it can be closed. Thank you! If this is an "epic" issue, then please add the "epic" label to this issue. |
## Summary Initial skill evaluation dataset for `cuopt-lp-milp-api-python` at `skills/cuopt-lp-milp-api-python/evals/evals.json`. 99 entries adapted from the [microsoft/OptiGuide](https://github.com/microsoft/OptiGuide) IndustryOR corpus (MIT, attribution in `evals/SOURCES.md`). - `ground_truth` is the numeric optimal value; rubric requires exact match to the precision shown (no tolerance) - `expected_behavior` is generic across all entries — does not pre-categorize as LP vs MILP - Each entry has a `source` field referencing the dataset row for traceability QP eval set is out of scope (the corpus has no genuine QP problems) and will follow in a separate PR. Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - Ishika Roy (https://github.com/Iroy30) URL: NVIDIA#1172
…1182) ## Summary - Declare `CUOPT_SLACK_MENTION_ID` as an optional `workflow_call` secret on the reusable `nightly-summary` workflow and forward it to the run-step env. - Pass it from `test.yaml` to the `nightly-summary` job alongside the other Slack secrets. ## Why `ci/nightly_summary.sh` already forwards `CUOPT_SLACK_MENTION_ID` to `ci/utils/generate_slack_payloads.py`, which uses it to prefix `<@id>` mentions on **new failures** and **new flaky tests**. Until now the env var was never populated in CI, so the mention was always empty. With this change, setting the `CUOPT_SLACK_MENTION_ID` repo secret (Slack user ID like `U01ABCDEF` or group handle like `S01ABCDEF`) will ping the configured handle. Leaving it unset preserves the no-mention default. ## Test plan - [ ] Add `CUOPT_SLACK_MENTION_ID` repo secret. - [ ] Trigger a nightly run (or `workflow_dispatch` on `nightly-summary`) and confirm the Slack message includes the `<@id>` mention on new failures. - [ ] Verify behavior is unchanged when the secret is unset (no mention).
…DIA#1020) This PR simplify the pseudo cost class in such a way that both regular and deterministic B&B use the same code path as much as possible. It also disable mutexes and atomics when running in deterministic mode as each thread has its own snapshot of the pseudocost. It also move the routines related with the diving heuristics back to the `diving_heuristics.cpp`, renamed `branch_and_bound_worker.hpp` to `worker.hpp` to match the new file structure and moved `worker_pool_t` to a dedicated header. Regular mode (GH200, 10min): ``` ================================================================================ main-190326-2 (1) vs simplify-pseudocost (2) ================================================================================ ------------------------------------------------------------------------------------------------------------------------------ | | Run 1 | Run 2 | Abs. Diff. | Rel. Diff. (%) | ------------------------------------------------------------------------------------------------------------------------------ | Feasible 226 226 +0 --- | | Optimal 70 67 -3 --- | | Solutions with <0.1% primal gap 121 122 +1 --- | | Nodes explored (mean) 4283972.9121 4455377.8117 +171404.8996 +3.847 | | Nodes explored (shifted geomean) 6202.3471 7062.2682 +859.9210 +12.176 | | Relative MIP gap (mean) 0.3382 0.3337 -0.0045 -1.325 | | Relative MIP gap (shifted geomean) 0.1193 0.1166 -0.0027 -2.293 | | Solve time (mean) 450.2347 452.9154 +2.6806 +0.592 | | Solve time (shifted geomean) 221.4772 227.6381 +6.1609 +2.706 | | Primal gap (mean) 11.4459 11.0482 -0.3976 -3.474 | | Primal gap (shifted geomean) 0.6591 0.6008 -0.0582 -8.838 | | Primal integral (mean) 49.9109 54.6941 +4.7832 +8.745 | | Primal integral (shifted geomean) 11.5672 13.9826 +2.4153 +17.274 | ------------------------------------------------------------------------------------------------------------------------------ ``` Determinism mode (GH200, 5min): ``` ================================================================================ main-240426-determinism (1) vs simplify-pseudocost-determinism (2) ================================================================================ ------------------------------------------------------------------------------------------------------------------------------ | | Run 1 | Run 2 | Abs. Diff. | Rel. Diff. (%) | ------------------------------------------------------------------------------------------------------------------------------ | Feasible 179 179 +0 --- | | Optimal 45 46 +1 --- | | Solutions with <0.1% primal gap 64 64 +0 --- | | Nodes explored (mean) 1.556e+06 1.511e+06 -4.526e+04 -2.91 | | Nodes explored (shifted geomean) 1895 1900 +5.427 +0.286 | | Relative MIP gap (mean) 7.038 0.7827 -6.255 -88.9 | | Relative MIP gap (shifted geomean) 0.2039 0.1841 -0.01984 -9.73 | | Solve time (mean) 249.5 251.7 +2.153 +0.855 | | Solve time (shifted geomean) 153.8 160.6 +6.767 +4.22 | | Primal gap (mean) 39.8 39.43 -0.3723 -0.935 | | Primal gap (shifted geomean) 5.315 5.314 -0.001059 -0.0199 | | Primal integral (mean) 292 299.2 +7.201 +2.41 | | Primal integral (shifted geomean) 49.42 50.91 +1.486 +2.92 | ------------------------------------------------------------------------------------------------------------------------------ ``` Authors: - Nicolas L. Guidotti (https://github.com/nguidotti) Approvers: - Alice Boucher (https://github.com/aliceb-nv) - Trevor McKay (https://github.com/tmckayus) - Chris Maes (https://github.com/chris-maes) URL: NVIDIA#1020
## Summary Make the two matrix entries in `.github/workflows/nightly.yaml` independent so a missing release-branch ref no longer cancels the working `main`-branch nightly. **Why this matters now:** the matrix bumped to `release/26.06` in NVIDIA#1091 on 2026-04-14, but `release/26.06` has not been cut from `main` yet. Every nightly since has failed — the release entry 404s at the `gh api .../branches/release/26.06` lookup, the matrix's default `fail-fast: true` cancels the sibling `main` entry, and the wrapper job reports `failure` even though the `main` dispatch would have succeeded on its own. **Fix:** add `fail-fast: false` so each cuopt_branch dispatch runs independently. ```yaml strategy: fail-fast: false # ← added matrix: cuopt_branch: - "main" - "release/26.06" ``` The release entry will still go red until `release/26.06` is actually cut (or the line is reverted to a release branch that exists), but it no longer drags `main` with it. ## Test plan - [ ] Watch the next scheduled nightly — `main` matrix entry should dispatch and complete independent of the `release/26.06` entry's outcome. - [ ] Manual dispatch via `Run workflow` from Actions UI to verify both entries run to completion (or independent failure) before waiting for the cron. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - James Lamb (https://github.com/jameslamb) URL: NVIDIA#1186
…family (NVIDIA#1183) ## Summary Collapses the LP/MILP and QP skills into a single **numerical-optimization** skill family across all surfaces (formulation + Python / C / CLI APIs). cuOpt's solver API is shared across LP, MILP, and QP — keeping per-problem-type skills with overlapping triggers just creates an activation-routing problem for the agent. Two commits on this branch: 1. **`0bf8ad4b`** — Combine `lp-milp-formulation` and `qp-formulation` into `numerical-optimization-formulation` (concepts + parsing + patterns). 2. **`af70748c`** — Combine the per-interface API skills: - `cuopt-lp-milp-api-python` + `cuopt-qp-api-python` → `cuopt-numerical-optimization-api-python` - `cuopt-lp-milp-api-c` + `cuopt-qp-api-c` → `cuopt-numerical-optimization-api-c` - `cuopt-lp-milp-api-cli` + `cuopt-qp-api-cli` → `cuopt-numerical-optimization-api-cli` Renames go through `git mv` so history (and the existing `evals/` dataset) is preserved. QP-specific assets and the QP examples doc were folded into the renamed Python skill (`resources/qp_examples.md`); QP C and CLI had no standalone source files, so their content became small sub-sections in the merged SKILL.md. Cross-refs updated: `AGENTS.md` (also covers `.claude/CLAUDE.md` via symlink), `.claude-plugin/marketplace.json`, `skills/skill-evolution/SKILL.md`, all 99 `expected_skill` values in the Python evals dataset, and `evals/SOURCES.md`. Per-skill SKILL.md additions for QP coverage (Python: decision table, portfolio example, MINIMIZE-only / continuous-only / Q-PSD rules, status note, two new Common Issues rows; C: QP-via-C-API section; CLI: QP-via-CLI section). ## Test plan - [x] `ci/utils/validate_skills.sh` passes - [x] `pre-commit run` passes (all hooks: end-of-files, trailing whitespace, JSON, ruff, copyright, hardcoded version, sync-skills-version, validate-agent-skills) - [x] Merged Python SKILL.md ~278 lines, below the resources/ split threshold - [x] No remaining references to `cuopt-{lp-milp,qp}-api-{python,c,cli}` anywhere in the tree - [ ] Verify CI passes --------- Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
…VIDIA#1187) ## Summary - Detect user-group (subteam) IDs by their `S` prefix and emit `<!subteam^S...>` syntax. - User IDs (prefix `U`/`W`) continue to use `<@U...>`. - Update the contract comment to spell out both forms and warn that handle names alone will not ping. ## Why Today's nightly Slack post (2026-05-07) showed `@cuopt-ci-team` rendered as plain text — the message arrived but produced no notification. Root cause: `ci/utils/generate_slack_payloads.py` always wrapped `CUOPT_SLACK_MENTION_ID` as `<@id>`, which is **user-only** syntax. Slack requires `<!subteam^SXXXXXXXX>` to ping a user group. Passing the group's handle name (`cuopt-ci-team`) or its `S...` ID with the old formatter both fail silently. ## Test plan - [ ] `CUOPT_SLACK_MENTION_ID=SXXXXXXXX` → next nightly with new failures pings the `cuopt-ci-team` group. - [ ] Sanity-check that a user ID (`U...`) still renders as `<@U...>` (no behavior change for that path). - [ ] Empty/unset `CUOPT_SLACK_MENTION_ID` produces no mention (existing behavior preserved). ## Notes Builds on NVIDIA#1182, which plumbs the `CUOPT_SLACK_MENTION_ID` secret through the workflow. Either order of merge works; this PR only touches the formatter.
This PR greatly extends the capabilities of batch PDLP. Former batch PDLP only supported having a single variable bounds being different per climber. It now supports: - Different constraints lower and upper bounds per climber - Different objective coefficients per climber - Different objective offset per climber - More than one variable bound difference per climber This PR also adds the support of per climber residual and first primal feasible to the Stable3 PDLP solver mode and its batch version. It allows to solve a batch of problems and stop once one or all the climbers have reached primal feasibility. All those combinations can be put together, resulting in a potential: Solve a batch of LPs all having different: constraints lower and upper bounds, objective coefficients, objective offset, variable bounds, using per constraint residual instead of the L2 norm and stopping once one, or all, climbers have reached primal feasibility. Authors: - Nicolas Blin (https://github.com/Kh4ster) - Trevor McKay (https://github.com/tmckayus) Approvers: - Akif ÇÖRDÜK (https://github.com/akifcorduk) - Trevor McKay (https://github.com/tmckayus) URL: NVIDIA#1152
…IA#1176) ## Summary Iterative refinement of the `cuopt-developer` skill driven by `astra-skill-eval` (NV-ACES) runs against its eval dataset. **SKILL.md content & structure** - Sharpened description and added a **Pre-flight Checks** block (CUDA driver compatibility, conda-env activation, `PARALLEL_LEVEL`, dataset pointer) at the top of *Build & Test*. - **Refusal Rules — Read First** moved to the top with literal scripts for the five categories that surfaced silent compliance in eval runs (package installs, CI bypass, outside-workspace writes, destructive commands, sudo). Refusals are absolute — no "with approval" escape (per CodeRabbit review). - **Compartmentalized into `resources/`**: `build_and_test.md`, `contributing.md`, `conventions.md`, `troubleshooting.md`. SKILL.md drops from ~4400 → ~1500 tokens. **Sibling-skill scoping (eval routing fix)** - `cuopt-user-rules` scoped to end users only (no longer competes on dev prompts). - `cuopt-installation-developer` **folded into `cuopt-developer`** as `resources/first_time_setup.md` after the install skill collapsed to ~30 lines once duplication was squeezed out (CUDA check + build/test commands already lived in cuopt-developer). 10 `inst-*` evals migrated into `cuopt-developer/evals/evals.json` (40 → 50, IDs preserved for provenance). Eliminates the routing collision the eval runs flagged as "borderline competitor on raw 'build from source' prompts". ### Eval impact - Astra Layer 1 static check: **78 → 84** (Grade C → B); large-skill warning cleared after compartmentalization. - Astra Harbor (opencode, group-mode skill-lift): aggregate with-skill score **0.62 → 0.80**; routing collisions on `cuopt-user-rules` eliminated. ### Out of scope - Four safety-refusal cases (`dev-006/021/025/037`) still fail in opencode runs because opencode often does not load `cuopt-developer` at all on those prompts — the new Refusal Rules block never reaches the model. Agent-side characteristic; tracked as a known issue. - End-to-end Harbor BYOT task (real `./build.sh` + `ctest`) deferred to a separate branch (needs GPU sandbox + custom Dockerfile + verifier scripts). ## Issue NA Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) - Ishika Roy (https://github.com/Iroy30) Approvers: - Ishika Roy (https://github.com/Iroy30) URL: NVIDIA#1176
## Summary Collapses three user-facing install skills into one **`cuopt-install`** skill covering Python, C, and server across pip, conda, and Docker. | Old | New | |---|---| | `cuopt-installation-common` (concepts) | merged → `cuopt-install` | | `cuopt-installation-api-python` | merged → `cuopt-install` | | `cuopt-installation-api-c` | merged → `cuopt-install` | | `cuopt-installation-developer` (build from source) | unchanged here — being folded into `cuopt-developer` in NVIDIA#1176 | Two commits: 1. **Consolidation** — collapse the three user-install skills into a single `cuopt-installation` skill. 2. **Rename** — `cuopt-installation` → `cuopt-install` (shorter, matches `pip install` mental model, removes residual audience ambiguity). ## Why - System requirements, package-manager decisions, and verification surface are **shared across interfaces**; splitting them created duplicated content and ambiguous activation when an agent doesn't yet know which interface the user wants. - Both API skills already declared **"Standalone; no common skill"** — the common skill was vestigial. - Mirrors the consolidation pattern from NVIDIA#1183 (numerical-optimization skill family). - Renaming to `cuopt-install` follows the `pip install` convention; the developer / build-from-source path lives elsewhere (`cuopt-developer`, post-NVIDIA#1176). ## Drive-by fixes (caught while drafting) - **C API was missing pip support.** `libcuopt-cu12` and `libcuopt-cu13` are real PyPI packages (see `dependencies.yaml:436,441`); the old `cuopt-installation-api-c` skill only documented conda. - **C API conda command was wrong.** Old skill said `conda install ... cuopt`, but `cuopt` is the **Python** conda package. The C package is `libcuopt` (`dependencies.yaml:423`). ## Cross-refs updated - `AGENTS.md` — skill index reorganized; new "Installation" section with `cuopt-install`. - `.claude-plugin/marketplace.json` — three plugin entries → one. - `skills/cuopt-installation-developer/evals/evals.json` — two references in `ground_truth` / `expected_behavior` updated to `cuopt-install`. ## Coordination with NVIDIA#1176 Both PRs touch `AGENTS.md`, `.claude-plugin/marketplace.json`, and `skills/cuopt-installation-developer/evals/evals.json`. Whichever lands second will need a small rebase (drop the eval edit since NVIDIA#1176 deletes that file; flip the "Installation" section in AGENTS.md). The merged eval cases that NVIDIA#1176 migrates into `cuopt-developer/evals/evals.json` may also need a one-line touch-up to point at `cuopt-install` rather than the old skill names. ## Test plan - [ ] Skill registry loads — `cuopt-install` appears in available skills, the three old user-install names do not. - [ ] User asks "how do I install cuOpt for Python?" → activates `cuopt-install`, produces correct pip / conda command. - [ ] User asks "how do I install cuOpt for C?" → activates `cuopt-install`, produces correct `libcuopt-cu12/13` pip command and `libcuopt` conda command. - [ ] User asks "how do I build cuOpt from source?" → still activates `cuopt-installation-developer` (or `cuopt-developer` post-NVIDIA#1176). Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - Ishika Roy (https://github.com/Iroy30) URL: NVIDIA#1189
I just saw a case where the agent refused to run the standard build process because build.sh installs cuopt packages into the python environment. Plus, there are autorun environments where we do want the agent to install packages autonomously. This type of instruction doesn't belong at the individual package level. Authors: - Miles Lubin (https://github.com/mlubin) Approvers: - Ramakrishnap (https://github.com/rgsl888prabhu) URL: NVIDIA#1200
…NVIDIA#1181) Similar to upstream changes in `shared-workflows`, this PR cleans up and annotates all of the workflows and adds the `zizmor` linter to make sure changes are checked. Part of rapidsai/build-planning#275 Authors: - Gil Forsyth (https://github.com/gforsyth) - https://github.com/jakirkham - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - Ramakrishnap (https://github.com/rgsl888prabhu) - https://github.com/jakirkham URL: NVIDIA#1181
EXPECT_DOUBLE_EQ was observed failing in CI with a 2.2e-15 error: https://github.com/NVIDIA/cuopt/actions/runs/25695199349/job/75445965119 ``` Expected equality of these values: solution.get_objective_value() Which is: 2.9999999999999978 3.0 Which is: 3 ``` Authors: - Miles Lubin (https://github.com/mlubin) Approvers: - Nicolas L. Guidotti (https://github.com/nguidotti) URL: NVIDIA#1199
…thon, thirdparty) (NVIDIA#1191) ## Summary When a test runner — gtest binary or pytest — is killed by a signal mid-run, it doesn't finalize its `--gtest_output=xml:` / `--junitxml` output. `ci/utils/nightly_report.py` classifies tests purely from those XML files, so a mid-run crash with no other captured failures was reported as **"All tests passed."** while the script exited non-zero. The GitHub step was correctly red, but the textual summary lied. Observed in a recent cpp run: `PDLP_TEST` reported five `[ FAILED ]` cases and then segfaulted, but the structured summary said `Classification: 459 passed, 0 failed, 0 errors, 0 flaky, 0 skipped` and concluded `All tests passed.` ## Changes ### Crash-XML synthesis (the actual fix) - `ci/utils/crash_helpers.sh` — add `write_pytest_crash_marker` helper that writes `<junitxml>-crash.xml` on pytest signal death, alongside (not over) any partial XML pytest may have emitted. - `ci/run_ctests.sh` — non-nightly crash path now calls `write_crash_xml` for the gtest binary; tracks failed binaries in a `FAILED_BINARIES` array and prints them at end of run. - `ci/run_cuopt_pytests.sh`, `ci/run_cuopt_server_pytests.sh` — call `write_pytest_crash_marker` on the non-nightly crash path. - `ci/thirdparty-testing/run_pulp_tests.sh`, `run_pyomo_tests.sh`, `run_cvxpy_tests.sh` — capture pytest's exit code and synthesize a crash XML on signal death. ### Per-step failure summaries - `ci/test_cpp.sh`, `ci/test_python.sh`, `ci/test_wheel_cuopt.sh`, `ci/test_wheel_cuopt_server.sh` — track each major step in a `FAILED_STEPS` array via `|| FAILED_STEPS+=(...)` and print a `==== FAILED TEST STEPS ====` block before the final `exit ${EXITCODE}`. ### Out of scope `test_notebooks.sh` and `test_doc_examples.sh` don't produce JUnit XML and exit non-zero on the first failure (notebooks via `exit 1`, doc-examples via its own EXIT-trap counter), so they don't have the same false-pass mode. ## Why this shape - Crash XMLs go to a separate `*-crash.xml` filename so we never clobber a partial XML emitted by gtest/pytest before the crash. - The nightly retry path is untouched — it already calls `write_crash_xml` per retry attempt and via `pytest_crash_isolate`. This PR plugs the gap on the non-nightly path (PR runs and any non-`nightly` `RAPIDS_BUILD_TYPE`). - Step summaries print to stdout only (per maintainer preference). `nightly_report.py` continues to write the structured markdown / HTML / GITHUB_STEP_SUMMARY. ## Test plan - [ ] On a deliberately-crashing gtest binary, verify `<test_name>-crash.xml` is written and `nightly_report.py` lists it as a failure. - [ ] On a deliberately-crashing pytest invocation, verify `<junit-name>-crash.xml` is written and the failure surfaces in the report. - [ ] On a normal (non-crash) test failure, verify behavior is unchanged. - [ ] On a fully-passing run, verify no `FAILED gtest BINARIES` / `FAILED TEST STEPS` block is printed. - [ ] `pre-commit run --files ci/...` passes (shellcheck wired in). Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - Bradley Dice (https://github.com/bdice) URL: NVIDIA#1191
## Summary - Adds `skills/cuopt-install/evals/evals.json` with 15 behavior-style evals for the `cuopt-install` skill. Coverage: required questions, Python/C/server install (pip/conda/Docker), CUDA suffix matching, verification, the mandatory no-auto-install rule, GPU Compute Capability requirements, and the redirect to `cuopt-developer` for from-source builds. - Corrects a factual error in `cuopt-install/SKILL.md` (3 lines) and `cuopt-user-rules/SKILL.md` (table + note): `cuopt-cuXX` declares `libcuopt-cuXX` as a runtime dependency (see `python/cuopt/pyproject.toml:26`), so the C library and headers are already installed alongside the Python package. The skills previously stated the Python and C packages were strictly separate; the reverse (`libcuopt` alone, no Python) is the only true standalone case. `cuopt-install` was the only user-facing skill without an evals directory; this closes that gap. `cuopt-user-rules` has no evals (rules skill, not API), so no eval changes needed there. Style of the new evals matches `cuopt-developer/evals/evals.json` (question + ground-truth narrative + `expected_behavior` list). ## Test plan - [ ] JSON validates (`python -m json.tool skills/cuopt-install/evals/evals.json`) - [ ] Eval IDs are unique within the file - [ ] Eval harness picks up the new file - [ ] Spot-check install-005 against the corrected SKILL.md / user-rules guidance Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - Trevor McKay (https://github.com/tmckayus) URL: NVIDIA#1205
Join concurrent solver worker threads before rethrowing exceptions so std::thread destructors do not terminate the process. Add a deterministic regression test that exercises a PDLP validation error after concurrent workers start.
Prevents some core dumps and gives a more useful error message for the test_incumbent_callbacks flaky test failure.
Old failure:
```
| Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node | Gap | Time |
terminate called without an active exception
Fatal Python error: Aborted
Current thread 0x0000e727cfdde020 (most recent call first):
File "/pyenv/versions/3.11.15/lib/python3.11/site-packages/cuopt/linear_programming/solver/solver.py", line 98 in Solve
File "/pyenv/versions/3.11.15/lib/python3.11/site-packages/cuopt/utilities/exception_handler.py", line 24 in func
File "/__w/cuopt/cuopt/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py", line 87 in _run_incumbent_solver_callback
File "/__w/cuopt/cuopt/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py", line 112 in test_incumbent_get_callback
```
New failure:
```
=================================== FAILURES ===================================
_________________ test_incumbent_get_callback[/mip/swath1.mps] _________________
file_name = '/mip/swath1.mps'
@pytest.mark.parametrize(
"file_name",
[
("/mip/swath1.mps"),
("/mip/neos5-free-bound.mps"),
],
)
def test_incumbent_get_callback(file_name):
> _run_incumbent_solver_callback(file_name, include_set_callback=False)
tests/linear_programming/test_incumbent_callbacks.py:112:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/linear_programming/test_incumbent_callbacks.py:87: in _run_incumbent_solver_callback
solution = solver.Solve(data_model_obj, settings)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/conda/envs/test/lib/python3.12/site-packages/cuopt/utilities/exception_handler.py:48: in func
raise e
/opt/conda/envs/test/lib/python3.12/site-packages/cuopt/utilities/exception_handler.py:24: in func
return f(*args, **kwargs)
^^^^^^^^^^^^^^^^^^
/opt/conda/envs/test/lib/python3.12/site-packages/cuopt/linear_programming/solver/solver.py:98: in Solve
s = solver_wrapper.Solve(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> ???
E RuntimeError: CUDA error encountered at: file=/tmp/conda-bld-output/bld/rattler-build_libmps-parser/work/cpp/src/pdlp/utilities/ping_pong_graph.cu line=57: call='cudaStreamEndCapture(stream_view_.value(), &even_graph)', Reason=cudaErrorStreamCaptureInvalidated:operation failed due to a previous error during capture
```
Authors:
- Miles Lubin (https://github.com/mlubin)
Approvers:
- Hugo Linsenmaier (https://github.com/hlinsen)
URL: NVIDIA#1206
After the OpenMP transition (NVIDIA#1099), the determinism mode uses a nested `omp parallel` regiom, which are disabled by default. Hence, the determinism mode is only using a single thread in the current implementation. This PR fixes that. Authors: - Nicolas L. Guidotti (https://github.com/nguidotti) Approvers: - Akif ÇÖRDÜK (https://github.com/akifcorduk) URL: NVIDIA#1201
## Issue Authors: - Alice Boucher (https://github.com/aliceb-nv) - Akif ÇÖRDÜK (https://github.com/akifcorduk) Approvers: - Akif ÇÖRDÜK (https://github.com/akifcorduk) - Nicolas L. Guidotti (https://github.com/nguidotti) URL: NVIDIA#1179
Opened NVIDIA#1207 to track the fix. Authors: - Miles Lubin (https://github.com/mlubin) Approvers: - Ramakrishnap (https://github.com/rgsl888prabhu) - Nicolas L. Guidotti (https://github.com/nguidotti) URL: NVIDIA#1208
This PR removes most of the unused/one-time-used device_vectors in cuPDLPx to reduce the memory footprint. It allows to run bigger problems without running out of memory Authors: - Bulle Mostovoi (https://github.com/Bubullzz) Approvers: - Miles Lubin (https://github.com/mlubin) - Nicolas Blin (https://github.com/Kh4ster) - Ramakrishnap (https://github.com/rgsl888prabhu) URL: NVIDIA#1153
## Summary - install only the Boost components needed by cuOpt's PaPILO dependency on Ubuntu - add `libboost-iostreams-dev` and `libboost-serialization-dev` explicitly rather than `libboost-dev` - skip `libboost-program-options-dev` -- this is optional and not needed for PaPILO with the options we provide (`PAPILO_NO_BINARIES=ON`) Authors: - Bradley Dice (https://github.com/bdice) - James Lamb (https://github.com/jameslamb) - Miles Lubin (https://github.com/mlubin) Approvers: - James Lamb (https://github.com/jameslamb) URL: NVIDIA#1165
early_cpufj and early_gpufj capture early_best_objective, early_best_user_obj, early_best_user_assignment, and early_callback_mutex by reference via the callbacks. They should be destructed before the values they capture by reference for safety. Authors: - Miles Lubin (https://github.com/mlubin) Approvers: - Rajesh Gandham (https://github.com/rg20) URL: NVIDIA#1216
early_fj_start is a local variable and should be captured by value since the callback outlives the local scope. Authors: - Miles Lubin (https://github.com/mlubin) Approvers: - Rajesh Gandham (https://github.com/rg20) URL: NVIDIA#1214
Issue: rapidsai/build-planning#277 Authors: - Kyle Edwards (https://github.com/KyleFromNVIDIA) Approvers: - Bradley Dice (https://github.com/bdice) URL: NVIDIA#1210
### Summary - Fix typo in NodeInfo location bound assert message in cpp/src/routing/structures.hpp. - Update to 32768 to match the existing check location < (1 << 15). - No behavior change: message correction for debugging clarity. Authors: - Daniel (https://github.com/aycsi) - Miles Lubin (https://github.com/mlubin) Approvers: - Miles Lubin (https://github.com/mlubin) - Rajesh Gandham (https://github.com/rg20) URL: NVIDIA#1218
## Summary - The concurrency group in `build.yaml` was keyed on `workflow + ref`, so a push to `main` (or `release/*`) would cancel an in-progress nightly-dispatched `build.yaml` run on the same branch — and the reverse was equally true. - Add the `build_type` input to the group key (falling back to `'branch'` when `inputs` is unset on `push`) so nightly and branch builds occupy separate groups. - Same-type, same-ref runs still cancel older runs (e.g. rapid pushes to `main` still supersede each other). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - James Lamb (https://github.com/jameslamb) URL: NVIDIA#1211
Contributes to rapidsai/build-planning#265 Closes NVIDIA#1155. * uses CUDA 13.2.0 to build and test * updates to CUDA 13.2.0 devcontainers ## Notes for Reviewers This switches GitHub Actions workflows to the `cuda-13.2.0` branch from here: rapidsai/shared-workflows#545 A future round of PRs will revert that back to `main`, once all of RAPIDS is migrated. Authors: - Bradley Dice (https://github.com/bdice) - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - James Lamb (https://github.com/jameslamb) - Ramakrishnap (https://github.com/rgsl888prabhu) URL: NVIDIA#1198
Boosts performance when many problems are scheduled in a loop with large matrices Authors: - Hugo Linsenmaier (https://github.com/hlinsen) Approvers: - Ramakrishnap (https://github.com/rgsl888prabhu) - Rajesh Gandham (https://github.com/rg20) URL: NVIDIA#1217
Merge the standalone libmps_parser C++ library and the cuopt-mps-parser Python wheel into the main cuopt build, eliminating the separate distribution while keeping all functionality. The parser sources now live under cpp/src/io/ and the public headers under cpp/include/cuopt/linear_programming/io/, matching the new namespace cuopt::linear_programming::io.
C++:
- Move cpp/libmps_parser/{src,include,tests}/ into the cuopt tree.
- Drop the separate libmps_parser.so target; parser sources compile into libcuopt.so via cpp/src/io/CMakeLists.txt.
- Hoist BZip2/ZLIB find_package + CUOPT_PARSER_WITH_{BZIP2,ZLIB} options to the top-level cpp build.
- Rename namespace cuopt::mps_parser -> cuopt::linear_programming::io across all sources, headers, tests, and benchmarks.
- Wire the gtest suite (mps_parser_test.cpp) into cpp/tests/linear_programming/CMakeLists.txt.
Python:
- Move python/cuopt/cuopt/linear_programming/cuopt_mps_parser/ into python/cuopt/cuopt/linear_programming/mps_parser/, importable as cuopt.linear_programming.mps_parser (and re-exported as ParseMps/toDict at the linear_programming package level).
- Fold data_model/ cython binding into the main cuopt wheel; relink data_model and solver cython modules to cuopt::cuopt.
- Update cython .pxd extern paths and namespace strings to cuopt/linear_programming/io/ and cuopt::linear_programming::io.
- Delete the standalone parser-wheel scaffolding (pyproject.toml, CMakeLists.txt, README, LICENSE, _version.py).
- Update test imports across test_parser.py, test_lp_solver.py, test_incumbent_callbacks.py, test_cpu_only_execution.py, test_pdlp_warmstart.py, plus cuopt_self_host_client.py, problem.py, and regression/benchmark_scripts/utils.py.
Build / packaging / CI:
- Drop the libmps_parser target and the FIND_MPS_PARSER_CPP flag from build.sh; help text and default action updated accordingly.
- Delete conda/recipes/mps-parser/ and the libmps-parser output (plus all pin_subpackage references) in conda/recipes/libcuopt/recipe.yaml.
- Drop cuopt-mps-parser =${version} from conda/recipes/cuopt/recipe.yaml.
- Remove the wheel-build-cuopt-mps-parser job, mps_parser_filter, and ci/build_wheel_cuopt_mps_parser.sh from CI; clean up cuopt_mps_parser wheelhouse downloads/installs from the remaining ci/*.sh scripts.
- Drop cuopt-mps-parser dependency entries from python/{cuopt,libcuopt, cuopt_self_hosted}/pyproject.toml and dependencies.yaml; remove the py_*_cuopt_mps_parser pyproject groups and depends_on_mps_parser.
- Update Dockerfile and ci/release/update-version.sh package lists.
MPS/QPS parsing functionality is now bundled directly into the `cuopt` Python package. The standalone `cuopt-mps-parser` package (and its `cuopt_mps_parser` and `data_model` import names) are no longer published.
If you depend on the parser, you need to update both your environment and your imports.
### Environment
**Conda users.** Remove `cuopt-mps-parser` from your environment files; `cuopt` now bundles the parser:
```yaml
# conda environment file (before)
dependencies:
- cuopt=26.6
- cuopt-mps-parser=26.6 # <- delete this line
# (after)
dependencies:
- cuopt=26.6
```
**pip users.** Remove `cuopt-mps-parser` from your `requirements.txt` / `pyproject.toml` dependencies. `cuopt` (and `libcuopt`, `cuopt-server`,
`cuopt-sh-client`) no longer require it transitively:
```diff
cuopt-cu13==26.6.*
-cuopt-mps-parser==26.6.*
```
Run `pip uninstall cuopt-mps-parser` in existing environments to clear the old install.
### Code
The functions and classes are unchanged — only the import paths moved. Update as follows:
| Before | After |
| --- | --- |
| `import cuopt_mps_parser` | `from cuopt.linear_programming import mps_parser` |
| `cuopt_mps_parser.ParseMps(...)` | `mps_parser.ParseMps(...)` |
| `cuopt_mps_parser.toDict(...)` | `from cuopt.linear_programming.mps_parser import toDict` then `toDict(...)` |
| `cuopt_mps_parser.parser_wrapper.DataModel` | `mps_parser.parser_wrapper.DataModel` |
| `from cuopt_mps_parser.utilities import InputValidationError` | `from cuopt.linear_programming.mps_parser.utilities import InputValidationError` |
| `from data_model import DataModel` | `from cuopt.linear_programming.data_model import DataModel` |
For convenience, `ParseMps` is also re-exported at the package level:
```python
from cuopt.linear_programming import ParseMps
```
Authors:
- Miles Lubin (https://github.com/mlubin)
- Ramakrishnap (https://github.com/rgsl888prabhu)
Approvers:
- Trevor McKay (https://github.com/tmckayus)
- Yuwen Chen (https://github.com/yuwenchen95)
URL: NVIDIA#1193
…VIDIA#1224) Updates `skills/cuopt-developer` to capture the reviewer feedback from NVIDIA#1194: - Expand the PR-description rule with an explicit "don't include" list (how-it-works walkthroughs, file tables, exhaustive test-plan checklists). - Add a new "Editing CI scripts and workflows" section: prefer extending existing scripts, don't restate framework defaults, no fallback values for required inputs, hard-code GitHub URLs, validate early, split chained bash commands. Goal is to keep future agent-authored PRs that touch `ci/` or `.github/workflows/` from generating the same review round-trips. Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - Trevor McKay (https://github.com/tmckayus) - Miles Lubin (https://github.com/mlubin) URL: NVIDIA#1224
## Summary - Fix an out-of-bounds host read in `adapted_sol_t::priority_remove_diff_routes` by sorting route ids directly on `routes[id].length`. - The old comparator indexed a compact `route_priority` vector by route id, which could corrupt the host heap and intermittently abort `ROUTING_TEST`. Closes NVIDIA#1221. Closes NVIDIA#866. Closes NVIDIA#783. ## Validation Running many times under ASan no longer triggers the SIGABRT, though it was very hard to reproduce the previous failure before this fix. Authors: - Bradley Dice (https://github.com/bdice) Approvers: - Hugo Linsenmaier (https://github.com/hlinsen) URL: NVIDIA#1222
…job level (NVIDIA#1229) ## Summary Closes **6 SonarQube findings** on `main` — 1 CRITICAL VULN and 5 MAJOR VULNs — without changing runtime behavior. ### `ci/test_self_hosted_service.sh` — suppress `shell:S4830` The `curl -k` on the health probe is intentional: this same script generates a self-signed cert and starts the cuOpt server two lines earlier, so there is no real TLS endpoint to validate against (subsequent `cuopt_sh` calls *do* validate using the test CA in `$CLIENT_CERT`). Added a `# NOSONAR` marker with rationale. ### `.github/workflows/build_test_publish_images.yaml` — job-scoped permissions Moved the workflow-level `permissions:` block to per-job blocks (rule `githubactions:S8264`/`S8233`). After reading the two reusable workflows (`build_images.yaml`, `test_images.yaml`), every job only does `actions/checkout` + DockerHub/NGC logins via username/password secrets — no OIDC, no GHCR pull, no artifact download, no PR API. So each job is reduced to `contents: read` only, dropping unused `actions: read`, `id-token: write`, `packages: read`, `pull-requests: read`. Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - Bradley Dice (https://github.com/bdice) URL: NVIDIA#1229
|
For some reason I pulled in a ton of unwanted changes. Closing and moving to PR #1237 |
When -inf <= x_j <= inf and we add implied bounds on x_j, we will compute a dual solution (u, w) that satisfies
A^T u + w = c + Qx
but w_j could be nonzero. When x_j is free, we need to enforce that the corresponding reduced cost is zero. We do this by transforming (u, w) into (y, z) such that
A^T y + z = c + Qx
with z_j = 0 for all j such that -inf < x_j < inf.
We also add a unit test on a small QP to confirm the fix.