Java bindings for LP/MIP/QP#1524
Conversation
📝 WalkthroughWalkthroughThis PR adds a standalone Java module implementing JNI-based cuOpt LP, MILP, QP, and quadratic-constraint APIs. It includes modeling and solver classes, native integration, build and CI workflows, tests, and Sphinx documentation. ChangesJava JNI Bindings Implementation
Estimated code review effort: 5 (Critical) | ~120 minutes Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
mlubin
left a comment
There was a problem hiding this comment.
I saw the PR is closed, sending my comments as I had them already written up.
| import java.util.List; | ||
|
|
||
| /** Compatibility entry point for Python's deprecated LP BatchSolve API. */ | ||
| public final class BatchSolve { |
There was a problem hiding this comment.
No need to add a java wrapper for the API that we're going to delete.
|
|
||
| extern "C" { | ||
|
|
||
| cuopt_int_t cuOptLoadParametersFromFile(cuOptSolverSettings settings, const char* path); |
There was a problem hiding this comment.
We should discuss merging these extensions into the C API.
|
|
||
| import java.util.Arrays; | ||
|
|
||
| public final class PDLPWarmStartData { |
There was a problem hiding this comment.
Why are we exposing PDLPWarmStartData? I think this is internal.
| @@ -0,0 +1,1367 @@ | |||
| /* | |||
| * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |||
There was a problem hiding this comment.
I'd recommend avoiding a test dependency on the python interface. The java interface should stand on its own.
| @@ -0,0 +1,25 @@ | |||
| # cuOpt Java bindings (beta) | |||
|
|
|||
| This directory is an isolated, customer-specific beta module for the cuOpt | |||
There was a problem hiding this comment.
Is this how we want to ship it?
There was a problem hiding this comment.
We would want to follow cuvs and try to publish to maven https://mvnrepository.com/artifact/com.nvidia.cuvs/cuvs-java
| =================================== | ||
|
|
||
| The Java LP/QP bindings are in the package | ||
| ``com.nvidia.cuopt.linearprogramming``. The public API is documented below by |
There was a problem hiding this comment.
Use com.nvidia.cuopt.mathematicalprogramming (or mathematical_programming if java conventions allow). The linear_programming name is deprecated. We changed it on the C++ side and someday will change it on the python side.
|
Sorry that was an accident. Reopening. |
| @@ -0,0 +1,28 @@ | |||
| /home/cbrissette/cuopt/java/cuopt/src/main/java/com/nvidia/cuopt/linearprogramming/BatchSolve.java | |||
There was a problem hiding this comment.
Do we need these files ? may be we can delete all the run time files so developers can concentrate on main parts.
ramakrishnap-nv
left a comment
There was a problem hiding this comment.
Focused review on APIs and shipping (vs how cuvs ships Java).
APIs: the surface is broad and, pleasingly, closely in sync with the Python API — the algebraic Problem layer matches Python's (camelCase) modeling methods almost 1:1, and DataModel maps cleanly (snake_case→camelCase). A few parity gaps and Java-idiom nits are noted inline.
Shipping: the main blockers — don't commit target/, and wire the build into CI/release the way cuvs does (ci/build_java.sh/ci/test_java.sh, dependencies.yaml java key, workflow jobs, version marker, docs toctree).
Non-blocking review comments below.
| @@ -0,0 +1,34 @@ | |||
| com/nvidia/cuopt/linearprogramming/SolverSettings$NativeHandle.class | |||
There was a problem hiding this comment.
Build output committed. The whole java/cuopt/target/ tree (~51 files: .class, surefire-reports/, maven-status/, generated-sources/) is Maven output and shouldn't be in git. Please remove it and add a java/.gitignore (target/, *.iml, hs_err*.log), as cuvs does.
There was a problem hiding this comment.
These should now be excluded.
| @@ -0,0 +1,65 @@ | |||
| #!/usr/bin/env bash | |||
There was a problem hiding this comment.
Not wired into CI/release — this is the "must be built on its own" gap. Consider ci/build_java.sh + ci/test_java.sh that pull the C++ artifact via rapids-download-from-github "$(rapids-artifact-name conda_cpp libcuopt cuopt --cuda "$RAPIDS_CUDA_VERSION")", plus java-build/conda-java-tests jobs in pr.yaml/test.yaml and a java file-key in dependencies.yaml — mirroring how cuvs ships Java.
There was a problem hiding this comment.
Mirrored your statements in the latest.
|
|
||
| <groupId>com.nvidia</groupId> | ||
| <artifactId>cuopt</artifactId> | ||
| <version>26.8.0-SNAPSHOT</version> |
There was a problem hiding this comment.
Use the RAPIDS version format 26.08.00 (not 26.8.0-SNAPSHOT) and add a version-update marker (cuvs: <!--CUVS_JAVA#VERSION_UPDATE_MARKER_START-->...<!--...END-->) so ci/release/update-version.sh bumps it. Also consider groupId com.nvidia.cuopt.
There was a problem hiding this comment.
Changed the version format, added an update marker, and groupId added. Additionally added ci/release/update-version.sh.
| @@ -0,0 +1,42 @@ | |||
| ==================================== | |||
There was a problem hiding this comment.
These Java docs aren't referenced by any Sphinx toctree, so they won't render. Please add cuopt-java/index to the top-level docs toctree.
| } | ||
|
|
||
| /** Return true for maximize and false for minimize, matching Python get_sense(). */ | ||
| public boolean getSense() { |
There was a problem hiding this comment.
Parity note (not a rename request): getSense() correctly matches the Python DataModel.get_sense() (bool, True=maximize) — good. Two parity gaps vs Python though: (1) Python puts set_initial_primal_solution/set_initial_dual_solution on DataModel, whereas here they're on SolverSettings; (2) Python DataModel also exposes getters this class seems to lack: get_quadratic_objective_{values,indices,offsets}, get_variable_names/get_row_names, get_objective_name/get_problem_name, get_ascii_row_types.
| resetSolvedValues(); | ||
| } | ||
|
|
||
| public Object getObjective() { |
There was a problem hiding this comment.
getObjective() returns Object — callers must downcast. Prefer a typed return (or overloads). Same for SolverSettings.getTypedParameter() / getMipCallbacks(). (The modeling API otherwise tracks the Python Problem layer 1:1 — nice.)
| private final int[] columnIndices; | ||
| private final double[] values; | ||
|
|
||
| public CsrMatrix(int[] rowOffsets, int[] columnIndices, double[] values) { |
There was a problem hiding this comment.
Arg order is (rowOffsets, columnIndices, values), but DataModel.setCsrConstraintMatrix is (values, indices, offsets) (matching Python's set_csr_constraint_matrix(A_values, A_indices, A_offsets)). This class is the odd one out — reversing it here is easy to transpose silently; consider aligning.
There was a problem hiding this comment.
Python's order seems wrong though. The order should be rowOffset, columnIndices, values.
There was a problem hiding this comment.
Maybe we should fix Python?
There was a problem hiding this comment.
Changed it to val, col-offsets, row-offsets. If you decide to change Python let me know and I'll revert it, but I am just aiming for parity here if possible.
| High-level model | ||
| ---------------- | ||
|
|
||
| ``Problem`` is the recommended entry point for models built in Java. |
There was a problem hiding this comment.
It's confusing to mix the terms model and Problem. I would use the term problem throughout and remove references to model
There was a problem hiding this comment.
Changed this to problem throughout.
| - Set a quadratic objective with optional linear and constant terms. | ||
| * - ``solve()`` / ``solve(SolverSettings)`` | ||
| - Convert the model to a native ``DataModel`` and return a ``Solution``. | ||
| * - ``toDataModel()`` |
There was a problem hiding this comment.
Is it necessary to have a DataModel? I think we should deprecate this.
There was a problem hiding this comment.
Fully depreciated in latest.
|
|
||
| The model also exposes ``getVariables``, ``getVariable``, ``getConstraints``, | ||
| ``getConstraint``, ``getNumVariables``, ``getNumConstraints``, | ||
| ``getNumNonZeros``, ``isMip``, ``isSolved``, ``getStatus``, |
There was a problem hiding this comment.
We should have consistent capitalization. Here we use isMip, but elsewhere we have MIPStats. I personally think we should always use MIP, LP and QP and cuOpt, even if it breaks Java convention. But failing that we should at least be consistent. Same for MPS and CSR
There was a problem hiding this comment.
Changed capitalization throughout.
|
|
||
| ``SolverSettings`` owns native solver configuration and implements | ||
| ``AutoCloseable``. Parameters can be set with the overloaded | ||
| ``setParameter`` methods for ``String``, ``int``, ``double``, and ``boolean`` |
There was a problem hiding this comment.
Here we are mixing the terms Settings and Parameters. I suggest we use Setting throughout for consistency.
| * primal and dual initial solutions; | ||
| * ``dumpParametersToFile`` and ``loadParametersFromFile``; | ||
| * ``toDict``; | ||
| * ``setPdlpWarmStartData``; and |
There was a problem hiding this comment.
Capitialization of PDLP is inconsistent: we have setPdlpWarmStartData and PDLPSolverMode.
There was a problem hiding this comment.
using PDLP throughout now.
| * ``getSolveTime`` and ``getProblemCategory``; and | ||
| * ``getVars`` when variable names are available. | ||
|
|
||
| LP solutions additionally expose ``getLpStats`` and PDLP warm-start data. |
There was a problem hiding this comment.
Inconsistent capitalization: getLpStats vs LPStats.
There was a problem hiding this comment.
Changed to "LP" throughout.
| MPS, batching, and errors | ||
| ------------------------- | ||
|
|
||
| ``DataModel.read``, ``DataModel.parseMps``, ``Problem.read``, and |
There was a problem hiding this comment.
Inconsistent capitalization: parseMps and readMPS
There was a problem hiding this comment.
Changed to MP throughout.
| 0.0, | ||
| new double[] {1.0, 1.0}, | ||
| matrix, | ||
| new byte[] {(byte) 'G'}, |
There was a problem hiding this comment.
Is it possible to do this with just 'G' instead of (byte) 'G'?
There was a problem hiding this comment.
Removed byte-casts in latest.
| } | ||
|
|
||
| Only ``LE`` and ``GE`` quadratic constraints are supported. Calling | ||
| ``QuadraticExpression.eq`` or adding an equality quadratic constraint raises |
There was a problem hiding this comment.
We should not even have a QuadraticExpression.eq
| quadratic matrices, MPS I/O, and solver results. | ||
|
|
||
| Quadratic constraints are supported for ``LE`` and ``GE`` constraints. Equality | ||
| quadratic constraints are rejected by the Java API. Dedicated SOCP modeling |
There was a problem hiding this comment.
Remove the sentence beginning with "Dedicated SOCP modeling ... " I'm not sure what it means and I don't think it's necessary to say this.
There was a problem hiding this comment.
Changed weird wording.
| ==================================== | ||
|
|
||
| NVIDIA cuOpt provides experimental Java bindings for linear programming (LP), | ||
| mixed-integer linear programming (MILP), quadratic programming (QP), and |
There was a problem hiding this comment.
What about QCQP and SOCP?
| package com.nvidia.cuopt.linearprogramming; | ||
|
|
||
| public enum PDLPSolverMode { | ||
| STABLE1(0), |
There was a problem hiding this comment.
I'm worried that these are going to get out of date. Is there any way that this can get the values from the c++ code? Rather than hard-coding it into the interface?
There was a problem hiding this comment.
Now pulling this from cuOptConstants which Maven generates from the C++ constants.h file.
| public enum ProblemCategory { | ||
| LP(0), | ||
| MIP(1), | ||
| IP(2); |
There was a problem hiding this comment.
We should deprecate IP across the whole code base.
| package com.nvidia.cuopt.linearprogramming; | ||
|
|
||
| public enum SolverMethod { | ||
| CONCURRENT(0), |
There was a problem hiding this comment.
I'm worried this is going to get out of date. Is there any way to get these values from the c++ code?
There was a problem hiding this comment.
Again updated to pull from C++ public header.
| package com.nvidia.cuopt.linearprogramming; | ||
|
|
||
| public enum TerminationStatus { | ||
| NO_TERMINATION(0), |
There was a problem hiding this comment.
I'm worried this is going to get out of date. Is there anyway to get these values from the C++ code?
There was a problem hiding this comment.
Again pulling from C++ public header.
| @@ -0,0 +1,151 @@ | |||
| package com.nvidia.cuopt.linearprogramming; | |||
|
|
|||
| public final class CuOptConstants { | |||
There was a problem hiding this comment.
Is this file autogenerated? If so, we should put a comment at the top saying it is autogenerated and add instructions on how to regenerate the file.
There was a problem hiding this comment.
It is autogenerated. Instructions are now in the README.
chris-maes
left a comment
There was a problem hiding this comment.
Thanks for adding the JAVA API. Let's make sure that capitalization is consistent before merging.
There was a problem hiding this comment.
Actionable comments posted: 12
🧹 Nitpick comments (9)
java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/DataModelIntegrationTest.java (1)
354-389: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUnused
expectSolutionValues/expectedObjectivefields for the QP case.
qp_diagonal_objectivepassesexpectSolutionValues=trueandexpectedObjective=Double.NaN, butverify()returns beforeassertSolution()for any casehasQuadraticObjective()(lines 37-41), so these values are never checked. Minor - not a functional issue given the explanatory comment, just slightly misleading at the call site.🤖 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 `@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/DataModelIntegrationTest.java` around lines 354 - 389, The qp_diagonal_objective CaseSpec is setting expectSolutionValues and expectedObjective even though DataModelIntegrationTest.verify() exits early for cases with hasQuadraticObjective() before assertSolution() runs, so these values are misleading at the call site. Update this test setup to either remove those unused expectations from the CaseSpec builder chain or move the checks into the quadratic-path assertions in verify()/assertSolution() using the CaseSpec and its hasQuadraticObjective()/withQuadraticObjective() flow.java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java (2)
20-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate test-scaffolding helpers across test classes.
assumeNativeLibrary()andassumeCudaDriverAvailable()are duplicated verbatim (with a small divergence - this file omitsredirectErrorStream(true),DataModelIntegrationTestincludes it) betweenNativeIntegrationTestandDataModelIntegrationTest. Consider extracting a shared test-support class to avoid drift between the two copies.♻️ Suggested extraction
// e.g. NativeTestSupport.java final class NativeTestSupport { static void assumeNativeLibrary() { String nativeDir = System.getProperty("cuopt.native.dir"); Assumptions.assumeTrue(nativeDir != null && !nativeDir.isBlank(), "cuopt.native.dir is unset"); Assumptions.assumeTrue( Files.exists(Path.of(nativeDir, System.mapLibraryName("cuopt_jni"))), "libcuopt_jni is not built"); } static void assumeCudaDriverAvailable() { try { Process process = new ProcessBuilder("nvidia-smi").redirectErrorStream(true).start(); Assumptions.assumeTrue(process.waitFor() == 0, "CUDA driver is unavailable"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Assumptions.assumeTrue(false, "CUDA driver check was interrupted"); } catch (Exception e) { Assumptions.assumeTrue(false, "CUDA driver check failed: " + e.getMessage()); } } }Also applies to: 172-178, 235-246
🤖 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 `@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java` around lines 20 - 26, The native test setup helpers are duplicated between NativeIntegrationTest and DataModelIntegrationTest, so extract the shared logic into a single test-support helper (for example, a NativeTestSupport class) and have both tests call it. Move assumeNativeLibrary() and assumeCudaDriverAvailable() into that shared class, keep the existing behavior consistent across both callers, and align the ProcessBuilder setup so the two copies do not drift (including the redirectErrorStream(true) detail where needed).
169-184: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winQuadratic objective built from an unrelated
Problem's variables.
x0/x1come from a freshly createdshellProblem, not fromtinyLP()'sDataModel. This only works becauseshell's variables happen to get indices 0/1, matchingtinyLP()'s variable count/order. A comment explaining the intentional index-only usage (or building the expression againsttinyLP's actual variable count) would make the contract explicit and less brittle to future edits oftinyLP().🤖 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 `@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java` around lines 169 - 184, The small QP test is building the quadratic objective from variables owned by a separate Problem, which relies on incidental matching indices and is brittle. Update solvesSmallQP to make the intent explicit by either constructing the expression from the DataModel/tinyLP variable context or adding a clear comment near the Problem/addVariable and QuadraticExpression.of usage that this is intentional index-only wiring, so future changes to tinyLP() don’t silently break the test.java/cuopt/scripts/build_native.sh (1)
12-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDuplicated JAVA_HOME auto-detection logic with
test.sh.This 7-line block (locate
javac/javafromPATH, deriveJAVA_HOME, validate) is repeated almost verbatim injava/cuopt/scripts/test.sh(lines 15-25). Consider extracting into a small sourced helper (e.g.scripts/_java_home.sh) to avoid the two copies drifting.🤖 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 `@java/cuopt/scripts/build_native.sh` around lines 12 - 23, The JAVA_HOME auto-detection logic in build_native.sh is duplicated in test.sh and should be shared to avoid drift. Extract the locate-derive-validate flow into a small sourced helper script (for example, a reusable Java-home setup helper) and have both build_native.sh and test.sh source it instead of maintaining separate copies; keep the existing JAVAC_PATH/JAVA_HOME detection and validation behavior centralized in that helper.java/cuopt/CMakeLists.txt (1)
11-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider
find_package(CUDAToolkit)instead of hand-rolled path resolution.Manually locating
libcudart.soand CUDA include dirs viaCONDA_PREFIX/targets/<arch>-linuxduplicates whatfind_package(CUDAToolkit)already does portably (and would fix the aarch64 path issue above for free).🤖 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 `@java/cuopt/CMakeLists.txt` around lines 11 - 52, The CUDA runtime and include path resolution in CMakeLists.txt is hand-rolled and should be replaced with CUDAToolkit discovery. Update the cuopt_jni setup to use find_package(CUDAToolkit) and then reference its imported targets/variables instead of hardcoding CONDA_PREFIX, targets/x86_64-linux, and /usr/local/cuda paths. Keep the existing cuOpt-specific checks, but switch target_link_libraries and include directories to symbols from CUDAToolkit so the JNI build stays portable across architectures..github/workflows/build.yaml (1)
72-95: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueGPU node requested for build-only job.
java-buildrunsci/build_java.shwithout--run-java-tests(build+package only, no GPU work), yet requestsnode_type: "gpu-l4-latest-1". If a CPU-only builder is available for the C++/CUDA-toolkit-linking step, using it here would avoid consuming GPU capacity for a job that doesn't execute GPU code.🤖 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 @.github/workflows/build.yaml around lines 72 - 95, The java-build workflow job is unnecessarily reserving a GPU node even though it only runs ci/build_java.sh for build/package and does not execute Java tests or GPU work. Update the java-build job’s node_type setting in the custom-job invocation to use a CPU-only builder if available, while keeping the rest of the matrix and artifact/upload configuration unchanged.java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/LinearExpression.java (1)
96-98: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winNo guard against division by zero in
dividedBy.
dividedBy(0.0)silently producesInfinity/NaNcoefficients that propagate into the constraint matrix passed to the native solver, rather than failing fast with a clear error at model-construction time.🛡️ Proposed guard
public LinearExpression dividedBy(double scalar) { + if (scalar == 0.0) { + throw new IllegalArgumentException("Cannot divide expression by zero"); + } return times(1.0 / scalar); }🤖 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 `@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/LinearExpression.java` around lines 96 - 98, Add a fail-fast guard in LinearExpression.dividedBy so scalar values of 0.0 are rejected before calling times(1.0 / scalar). Update the LinearExpression.dividedBy method to validate the input and throw a clear exception for zero (and any other invalid non-finite case if appropriate), so invalid coefficients cannot propagate into the native solver.java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/QuadraticExpression.java (1)
108-110: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winSame division-by-zero gap as
LinearExpression.dividedBy.Consider applying the same guard here for consistency.
🤖 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 `@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/QuadraticExpression.java` around lines 108 - 110, The QuadraticExpression.dividedBy method has the same missing division-by-zero guard as LinearExpression.dividedBy. Update QuadraticExpression.dividedBy(double scalar) to validate the scalar before calling times(1.0 / scalar), and make its behavior consistent with the corresponding linear expression method by rejecting zero (and any other unsupported values if applicable) with the same error handling pattern.java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.java (1)
165-216: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPublic API marked
@Deprecatedis load-bearing for the entire solve/writeMPS path.
toDataModel()is annotated@Deprecated(since = "26.08")with"Use Problem directly", yetsolve(SolverSettings)(line 226) andwriteMPS(String)(line 238) both call it internally on every invocation. This is confusing: the class's own primary API depends on a method it tells external users to stop using. Consider extracting a private/package-private builder (e.g.buildDataModel()) for internal use and keeping the publictoDataModel()as a thin deprecated wrapper, so the deprecation signal is accurate and internal callers don't trigger deprecation warnings.🤖 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 `@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.java` around lines 165 - 216, The deprecated public toDataModel() method is still being used as the internal implementation for solve(SolverSettings) and writeMPS(String), which makes the deprecation message misleading. Extract the shared conversion logic into a private/package-private builder method such as buildDataModel(), have solve and writeMPS call that internal helper, and keep toDataModel() as a thin deprecated wrapper so external callers still see the deprecation while internal paths avoid depending on the deprecated API.
🤖 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 @.github/workflows/build.yaml:
- Around line 63-71: The java-build-matrix job is still hardcoded to use
pull-request, which causes the wrong Java matrix for non-PR runs. Update the
workflow input in build.yaml so the uses: compute-matrix.yaml call for
java-build-matrix passes the same build_type used by the workflow instead of a
fixed value, keeping the matrix selection aligned with the trigger type.
In `@java/cuopt/CMakeLists.txt`:
- Around line 23-24: The CUOPT CUDA runtime and include paths are hardcoded to
x86_64 and will break supported aarch64 builds. Update the
CUOPT_CUDA_RUNTIME_DIR and related include-path logic in CMakeLists.txt to
derive the target architecture dynamically, using CMAKE_SYSTEM_PROCESSOR or CUDA
toolkit discovery instead of embedding x86_64-linux, so CUOPT_CUDART_LIBRARY
resolves correctly on both x86_64 and aarch64.
In
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/CSRMatrix.java`:
- Around line 15-22: Add explicit validation in CSRMatrix’s constructor before
calling Arrays.copyOf: reject null values, columnIndices, and rowOffsets with a
clear IllegalArgumentException, then validate CSR consistency by checking that
the rowOffsets array is non-empty, starts at 0, is monotonic, and that its last
entry matches values.length (and therefore the nnz count). Keep the existing
values/columnIndices length check, and ensure the constructor fails fast with a
descriptive message so malformed matrices cannot reach NativeCuOpt.createProblem
or setConstraintMatrix.
In
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/DataModel.java`:
- Around line 253-263: The quadratic objective cache in DataModel becomes stale
when mixing setQuadraticObjectiveMatrix(...) and
setQuadraticObjective(QuadraticExpression), because the latter updates native
state but leaves quadraticObjectiveValues/Indices/Offsets and
quadraticObjectiveMatrixSet unchanged. Update setQuadraticObjective(...) to keep
the cached CSR arrays and flag in sync with the new objective, and make sure the
getter methods getQuadraticObjectiveValues/Indices/Offsets() always reflect the
most recently set objective rather than old matrix data.
- Around line 413-500: The quadratic constraint naming logic in DataModel is
using insertion order, so after read() or parseMPS() leaves
quadraticConstraintNames empty, the next addQuadraticConstraint(...) can
overwrite an existing native constraint’s name. Fix this by initializing
quadraticConstraintNames from the loaded model in the parsing/load path, or by
associating names with the native row index instead of list position; update the
addQuadraticConstraint overloads and getQuadraticConstraints so they resolve
names using a stable identifier rather than append order.
In
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.java`:
- Around line 393-414: `updateConstraint` is missing the same ownership
validation that `updateObjective` uses for coefficient variables. In
`Problem.updateConstraint`, add a check in the `coefficients` loop to নিশ্চিত
each `Variable` key belongs to this `Problem`’s `variables` set and throw
`IllegalArgumentException` if not, before merging it into the
`LinearExpression`; keep the rest of the update flow in `updateConstraint`
unchanged.
- Around line 416-456: The updateObjective method in Problem is leaving the
linearObjective field and objectiveSet flag unchanged when coefficients are
provided before any objective has been initialized and constant is null, so
getObjective() stays stale. Add a branch in updateObjective that rebuilds
linearObjective from the current variable objective coefficients (or otherwise
synchronizes linearObjective) and marks objectiveSet true for this
coefficients-only path, while keeping the existing ObjectiveSense handling and
compatibility with objectiveCoefficients() and getObjective().
- Around line 39-55: `addVariable(...)` and `addConstraint(...)` only mark the
model unsolved, leaving stale solve metadata behind. Update both methods in
`Problem` to also clear cached solved state the same way `setObjective(...)`
does by invoking `resetSolvedValues()` after mutating `variables` or
`constraints`, so `getStatus()`, `getObjectiveValue()`, `getSolveTime()`, and
cached variable/constraint values no longer reflect an older solve.
- Around line 222-235: The solve(SolverSettings) flow in Problem leaves the
native Solution open if populateSolution(...) throws after dataModel.solve(...)
returns. Update this method so the Solution returned from
dataModel.solve(actualSettings) is always closed on the failure path, e.g. by
wrapping populateSolution(solution) in its own try/catch/finally and calling
solution.close() before rethrowing, while preserving the existing closeSettings
handling for SolverSettings.
- Around line 278-310: The constraint reconstruction logic in
Problem.read/Problem.readMPS is flattening ranged rows into a single LE/GE/EQ
constraint based on rhs[row], which drops the original interval bounds. Update
the row handling around the ConstraintSense selection and switch so rows with
differing constraintLowerBounds[row] and constraintUpperBounds[row] are either
preserved via a range-aware representation or explicitly rejected with a clear
error instead of being converted. Keep the existing single-sided path for
non-ranged rows and make the behavior around rowNames and addConstraint
consistent with the chosen handling.
In `@java/cuopt/src/main/native/cuopt_jni.cpp`:
- Around line 261-289: The JNI callback in mip_get_solution_callback leaks a
local reference because GetObjectClass(context->callback) is never paired with a
DeleteLocalRef. Store the jclass returned by GetObjectClass, use it for
GetMethodID, and delete that local ref before returning; keep the rest of the
callback flow in cuopt_jni.cpp unchanged.
- Around line 291-327: mip_set_solution_callback is leaking several JNI local
references and silently ignoring a solution-size mismatch. In this callback,
delete the local refs created by GetObjectClass, CallObjectMethod,
GetObjectClass on callback_solution, and GetObjectField for the solution array
before returning, ideally using a consistent cleanup path in
mip_set_solution_callback. Also add explicit handling for the values.size() !=
context->num_variables case in the same function: surface an error via the
existing JNI/error-reporting mechanism or logging instead of doing nothing, so
callback sizing bugs are diagnosable.
---
Nitpick comments:
In @.github/workflows/build.yaml:
- Around line 72-95: The java-build workflow job is unnecessarily reserving a
GPU node even though it only runs ci/build_java.sh for build/package and does
not execute Java tests or GPU work. Update the java-build job’s node_type
setting in the custom-job invocation to use a CPU-only builder if available,
while keeping the rest of the matrix and artifact/upload configuration
unchanged.
In `@java/cuopt/CMakeLists.txt`:
- Around line 11-52: The CUDA runtime and include path resolution in
CMakeLists.txt is hand-rolled and should be replaced with CUDAToolkit discovery.
Update the cuopt_jni setup to use find_package(CUDAToolkit) and then reference
its imported targets/variables instead of hardcoding CONDA_PREFIX,
targets/x86_64-linux, and /usr/local/cuda paths. Keep the existing
cuOpt-specific checks, but switch target_link_libraries and include directories
to symbols from CUDAToolkit so the JNI build stays portable across
architectures.
In `@java/cuopt/scripts/build_native.sh`:
- Around line 12-23: The JAVA_HOME auto-detection logic in build_native.sh is
duplicated in test.sh and should be shared to avoid drift. Extract the
locate-derive-validate flow into a small sourced helper script (for example, a
reusable Java-home setup helper) and have both build_native.sh and test.sh
source it instead of maintaining separate copies; keep the existing
JAVAC_PATH/JAVA_HOME detection and validation behavior centralized in that
helper.
In
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/LinearExpression.java`:
- Around line 96-98: Add a fail-fast guard in LinearExpression.dividedBy so
scalar values of 0.0 are rejected before calling times(1.0 / scalar). Update the
LinearExpression.dividedBy method to validate the input and throw a clear
exception for zero (and any other invalid non-finite case if appropriate), so
invalid coefficients cannot propagate into the native solver.
In
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.java`:
- Around line 165-216: The deprecated public toDataModel() method is still being
used as the internal implementation for solve(SolverSettings) and
writeMPS(String), which makes the deprecation message misleading. Extract the
shared conversion logic into a private/package-private builder method such as
buildDataModel(), have solve and writeMPS call that internal helper, and keep
toDataModel() as a thin deprecated wrapper so external callers still see the
deprecation while internal paths avoid depending on the deprecated API.
In
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/QuadraticExpression.java`:
- Around line 108-110: The QuadraticExpression.dividedBy method has the same
missing division-by-zero guard as LinearExpression.dividedBy. Update
QuadraticExpression.dividedBy(double scalar) to validate the scalar before
calling times(1.0 / scalar), and make its behavior consistent with the
corresponding linear expression method by rejecting zero (and any other
unsupported values if applicable) with the same error handling pattern.
In
`@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/DataModelIntegrationTest.java`:
- Around line 354-389: The qp_diagonal_objective CaseSpec is setting
expectSolutionValues and expectedObjective even though
DataModelIntegrationTest.verify() exits early for cases with
hasQuadraticObjective() before assertSolution() runs, so these values are
misleading at the call site. Update this test setup to either remove those
unused expectations from the CaseSpec builder chain or move the checks into the
quadratic-path assertions in verify()/assertSolution() using the CaseSpec and
its hasQuadraticObjective()/withQuadraticObjective() flow.
In
`@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java`:
- Around line 20-26: The native test setup helpers are duplicated between
NativeIntegrationTest and DataModelIntegrationTest, so extract the shared logic
into a single test-support helper (for example, a NativeTestSupport class) and
have both tests call it. Move assumeNativeLibrary() and
assumeCudaDriverAvailable() into that shared class, keep the existing behavior
consistent across both callers, and align the ProcessBuilder setup so the two
copies do not drift (including the redirectErrorStream(true) detail where
needed).
- Around line 169-184: The small QP test is building the quadratic objective
from variables owned by a separate Problem, which relies on incidental matching
indices and is brittle. Update solvesSmallQP to make the intent explicit by
either constructing the expression from the DataModel/tinyLP variable context or
adding a clear comment near the Problem/addVariable and QuadraticExpression.of
usage that this is intentional index-only wiring, so future changes to tinyLP()
don’t silently break the test.
🪄 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: 3ca4aa57-ac64-49cf-9f79-73f9782af4c5
📒 Files selected for processing (56)
.github/workflows/build.yaml.github/workflows/pr.yaml.github/workflows/test.yamlci/build_java.shci/release/update-version.shci/test_java.shdependencies.yamldocs/cuopt/source/cuopt-java/convex/convex-api.rstdocs/cuopt/source/cuopt-java/convex/convex-examples.rstdocs/cuopt/source/cuopt-java/convex/index.rstdocs/cuopt/source/cuopt-java/index.rstdocs/cuopt/source/cuopt-java/mip/index.rstdocs/cuopt/source/cuopt-java/mip/mip-api.rstdocs/cuopt/source/cuopt-java/mip/mip-examples.rstdocs/cuopt/source/cuopt-java/quick-start.rstdocs/cuopt/source/index.rstjava/.gitignorejava/cuopt/CMakeLists.txtjava/cuopt/README.mdjava/cuopt/TESTS.mdjava/cuopt/pom.xmljava/cuopt/scripts/build_native.shjava/cuopt/scripts/generate_constants.shjava/cuopt/scripts/test.shjava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/CSRMatrix.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Constraint.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/ConstraintSense.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/CuOptException.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/DataModel.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/LPStats.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/LinearExpression.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/MIPCallback.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/MIPCallbackSolution.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/MIPSetSolutionCallback.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/MIPSolutionCallback.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/MIPStats.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/NativeCuOpt.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/ObjectiveExpression.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/ObjectiveSense.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/PDLPSolverMode.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/ProblemCategory.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/QuadraticConstraint.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/QuadraticExpression.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Solution.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/SolverMethod.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/SolverSettings.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/TerminationStatus.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Variable.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/VariableType.javajava/cuopt/src/main/native/cuopt_java_native_api.cppjava/cuopt/src/main/native/cuopt_java_native_api.hppjava/cuopt/src/main/native/cuopt_jni.cppjava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/DataModelIntegrationTest.javajava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.javajava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemModelingTest.java
| public DataModel setQuadraticObjectiveMatrix(double[] values, int[] indices, int[] offsets) { | ||
| double[] copiedValues = copy(values); | ||
| int[] copiedIndices = copy(indices); | ||
| int[] copiedOffsets = copy(offsets); | ||
| NativeCuOpt.setQuadraticObjectiveMatrix(handle(), copiedValues, copiedIndices, copiedOffsets); | ||
| quadraticObjectiveValues = copiedValues; | ||
| quadraticObjectiveIndices = copiedIndices; | ||
| quadraticObjectiveOffsets = copiedOffsets; | ||
| quadraticObjectiveMatrixSet = true; | ||
| return this; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Stale cached quadratic-objective data after mixing setter APIs.
setQuadraticObjective(QuadraticExpression) writes the new objective to native but never updates quadraticObjectiveValues/Indices/Offsets or resets quadraticObjectiveMatrixSet. If a caller previously invoked setQuadraticObjectiveMatrix(...) (which sets the flag and caches the CSR arrays) and later calls setQuadraticObjective(...), subsequent getQuadraticObjectiveValues/Indices/Offsets() calls will silently return the earlier cached data instead of the objective that was just set.
🛡️ Proposed fix — keep cache in sync
public DataModel setQuadraticObjective(QuadraticExpression expression) {
NativeCuOpt.setQuadraticObjective(
handle(), quadraticRows(expression), quadraticColumns(expression), quadraticValues(expression));
+ quadraticObjectiveValues = quadraticValues(expression);
+ quadraticObjectiveIndices = quadraticColumns(expression);
+ // quadraticObjectiveOffsets requires row-offset construction consistent with expression's rows
+ quadraticObjectiveMatrixSet = true;
return this;
}Also applies to: 335-351, 407-411
🤖 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
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/DataModel.java`
around lines 253 - 263, The quadratic objective cache in DataModel becomes stale
when mixing setQuadraticObjectiveMatrix(...) and
setQuadraticObjective(QuadraticExpression), because the latter updates native
state but leaves quadraticObjectiveValues/Indices/Offsets and
quadraticObjectiveMatrixSet unchanged. Update setQuadraticObjective(...) to keep
the cached CSR arrays and flag in sync with the new objective, and make sure the
getter methods getQuadraticObjectiveValues/Indices/Offsets() always reflect the
most recently set objective rather than old matrix data.
| public DataModel addQuadraticConstraint(Constraint constraint) { | ||
| if (!constraint.isQuadratic()) { | ||
| throw new IllegalArgumentException("Quadratic constraint requires quadratic terms"); | ||
| } | ||
| if (constraint.getSense() == ConstraintSense.EQ) { | ||
| throw new IllegalArgumentException("Equality quadratic constraints are not supported"); | ||
| } | ||
| QuadraticExpression expression = constraint.getQuadraticExpression(); | ||
| LinearExpression linear = constraint.getLinearExpression(); | ||
| int[] linearIndices = new int[linear.getTerms().size()]; | ||
| double[] linearCoefficients = new double[linear.getTerms().size()]; | ||
| int i = 0; | ||
| for (var entry : linear.getTerms().entrySet()) { | ||
| linearIndices[i] = entry.getKey().getIndex(); | ||
| linearCoefficients[i] = entry.getValue(); | ||
| i++; | ||
| } | ||
| NativeCuOpt.addQuadraticConstraint( | ||
| handle(), | ||
| quadraticRows(expression), | ||
| quadraticColumns(expression), | ||
| quadraticValues(expression), | ||
| linearIndices, | ||
| linearCoefficients, | ||
| constraint.getSense().nativeValue(), | ||
| constraint.getRHS()); | ||
| quadraticConstraintNames.add(constraint.getConstraintName()); | ||
| return this; | ||
| } | ||
|
|
||
| public DataModel addQuadraticConstraint( | ||
| String rowName, | ||
| double[] linearValues, | ||
| int[] linearIndices, | ||
| double rhs, | ||
| double[] values, | ||
| int[] rows, | ||
| int[] columns, | ||
| ConstraintSense sense) { | ||
| if (sense == ConstraintSense.EQ) { | ||
| throw new IllegalArgumentException("Equality quadratic constraints are not supported"); | ||
| } | ||
| if (linearValues == null || linearIndices == null || linearValues.length != linearIndices.length) { | ||
| throw new IllegalArgumentException("linearValues and linearIndices must have the same length"); | ||
| } | ||
| if (values == null || rows == null || columns == null | ||
| || values.length != rows.length || values.length != columns.length) { | ||
| throw new IllegalArgumentException("quadratic COO arrays must have the same length"); | ||
| } | ||
| NativeCuOpt.addQuadraticConstraint( | ||
| handle(), | ||
| copy(rows), | ||
| copy(columns), | ||
| copy(values), | ||
| copy(linearIndices), | ||
| copy(linearValues), | ||
| sense.nativeValue(), | ||
| rhs); | ||
| quadraticConstraintNames.add(rowName == null ? "" : rowName); | ||
| return this; | ||
| } | ||
|
|
||
| public List<QuadraticConstraint> getQuadraticConstraints() { | ||
| Object[] nativeConstraints = NativeCuOpt.getQuadraticConstraints(handle()); | ||
| List<QuadraticConstraint> result = new ArrayList<>(nativeConstraints.length); | ||
| for (int i = 0; i < nativeConstraints.length; i++) { | ||
| Object[] entry = (Object[]) nativeConstraints[i]; | ||
| int rowIndex = ((int[]) entry[0])[0]; | ||
| String rowName = (String) entry[1]; | ||
| if (i < quadraticConstraintNames.size() && !quadraticConstraintNames.get(i).isEmpty()) { | ||
| rowName = quadraticConstraintNames.get(i); | ||
| } | ||
| ConstraintSense sense = ConstraintSense.fromNative(((byte[]) entry[2])[0]); | ||
| double rhs = ((double[]) entry[5])[0]; | ||
| result.add( | ||
| new QuadraticConstraint( | ||
| rowIndex, | ||
| rowName, | ||
| sense, | ||
| (double[]) entry[3], | ||
| (int[]) entry[4], | ||
| rhs, | ||
| (int[]) entry[6], | ||
| (int[]) entry[7], | ||
| (double[]) entry[8])); | ||
| } | ||
| return List.copyOf(result); | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant Java sources and inspect the DataModel and parser entry points.
git ls-files 'java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/*.java' | sed -n '1,120p'
echo
echo "=== DataModel references ==="
rg -n "quadraticConstraintNames|addQuadraticConstraint|getQuadraticConstraints|read\\(|parseMPS\\(" \
java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/DataModel.java \
java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming -S
echo
echo "=== Parser / loader references ==="
rg -n "parseMPS|read\\(|quadratic constraint|QuadraticConstraint|QPS|MPS" \
java/cuopt/src/main/java -S
echo
echo "=== Relevant DataModel excerpt ==="
sed -n '1,260p' java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/DataModel.java
echo
echo "=== Later DataModel excerpt ==="
sed -n '260,560p' java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/DataModel.javaRepository: NVIDIA/cuopt
Length of output: 33948
Quadratic constraint names can shift after parsing
read()/parseMPS() leave quadraticConstraintNames empty, so the first later addQuadraticConstraint(...) overrides the name of the first native quadratic constraint instead of the new one. Seed this list from the loaded model, or key names by native row index instead of insertion order.
🤖 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
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/DataModel.java`
around lines 413 - 500, The quadratic constraint naming logic in DataModel is
using insertion order, so after read() or parseMPS() leaves
quadraticConstraintNames empty, the next addQuadraticConstraint(...) can
overwrite an existing native constraint’s name. Fix this by initializing
quadraticConstraintNames from the loaded model in the parsing/load path, or by
associating names with the native row index instead of list position; update the
addQuadraticConstraint overloads and getQuadraticConstraints so they resolve
names using a stable identifier rather than append order.
| public void updateObjective(Map<Variable, Double> coefficients, Double constant, ObjectiveSense sense) { | ||
| if (coefficients != null) { | ||
| for (Map.Entry<Variable, Double> entry : coefficients.entrySet()) { | ||
| if (!variables.contains(entry.getKey())) { | ||
| throw new IllegalArgumentException("Objective variable does not belong to this problem"); | ||
| } | ||
| entry.getKey().setObjectiveCoefficient(entry.getValue()); | ||
| } | ||
| if (objectiveSet) { | ||
| LinearExpression updated = | ||
| LinearExpression.ofConstant(constant == null ? linearObjective.getConstant() : constant); | ||
| for (Map.Entry<Variable, Double> entry : linearObjective.getTerms().entrySet()) { | ||
| updated = updated.plus(entry.getKey(), coefficients.getOrDefault(entry.getKey(), entry.getValue())); | ||
| } | ||
| for (Map.Entry<Variable, Double> entry : coefficients.entrySet()) { | ||
| if (!linearObjective.getTerms().containsKey(entry.getKey())) { | ||
| updated = updated.plus(entry.getKey(), entry.getValue()); | ||
| } | ||
| } | ||
| linearObjective = updated; | ||
| if (quadraticObjective != null) { | ||
| QuadraticExpression updatedQuadratic = | ||
| new QuadraticExpression().constant(linearObjective.getConstant()); | ||
| for (Map.Entry<Variable, Double> entry : linearObjective.getTerms().entrySet()) { | ||
| updatedQuadratic = updatedQuadratic.plus(entry.getKey(), entry.getValue()); | ||
| } | ||
| for (QuadraticExpression.QuadraticTerm term : quadraticObjective.getQuadraticTerms()) { | ||
| updatedQuadratic = | ||
| updatedQuadratic.plus(term.getFirst(), term.getSecond(), term.getCoefficient()); | ||
| } | ||
| quadraticObjective = updatedQuadratic; | ||
| } | ||
| } else if (constant != null) { | ||
| linearObjective = LinearExpression.ofConstant(constant); | ||
| for (Variable variable : variables) { | ||
| if (variable.getObjectiveCoefficient() != 0.0) { | ||
| linearObjective = linearObjective.plus(variable, variable.getObjectiveCoefficient()); | ||
| } | ||
| } | ||
| objectiveSet = true; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
updateObjective leaves getObjective() stale when only per-variable coefficients are updated before any objective is set.
When coefficients != null, objectiveSet is false, and constant == null, the loop updates each Variable's own objectiveCoefficient (line 422), but neither linearObjective nor objectiveSet are touched — there's no corresponding branch for this case (only the objectiveSet == true branch and the objectiveSet == false && constant != null branch handle it). Since objectiveCoefficients() (used for solving, line 644) falls back to reading variable.getObjectiveCoefficient() when !objectiveSet, the actual solve is unaffected. However, getObjective() (line 470) returns the stale/empty linearObjective field, so introspection via problem.getObjective() will not reflect the coefficients that were just set — a real observable inconsistency for API consumers.
🤖 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
`@java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.java`
around lines 416 - 456, The updateObjective method in Problem is leaving the
linearObjective field and objectiveSet flag unchanged when coefficients are
provided before any objective has been initialized and constant is null, so
getObjective() stays stale. Add a branch in updateObjective that rebuilds
linearObjective from the current variable objective coefficients (or otherwise
synchronizes linearObjective) and marks objectiveSet true for this
coefficients-only path, while keeping the existing ObjectiveSense handling and
compatibility with objectiveCoefficients() and getObjective().
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java (1)
87-105: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
problemis never closed, and the objective relies on an implicit default sense.Every other test in this file wraps
Problemin try-with-resources; hereproblemis created via plain assignment and never closed, leaking whatever native/Java-side stateProblem.close()releases. Separately, nosetObjectivecall is made — the test relies onaddVariable's side-channel objective coefficient plus whateverObjectiveSenseProblemdefaults to, which is fragile if that default ever changes.🔧 Proposed fix
- Problem problem = new Problem("integer"); - Variable x = problem.addVariable(0, 10, 1.0, VariableType.INTEGER, "x"); - problem.addConstraint(LinearExpression.of(x).ge(1.0)); - - try (SolverSettings settings = new SolverSettings().setSetting(CuOptConstants.CUOPT_TIME_LIMIT, 10.0); - Solution solution = problem.solve(settings)) { - assertTrue(solution.isMIP()); - assertEquals(TerminationStatus.OPTIMAL, solution.getTerminationStatus()); - assertEquals(1.0, x.getValue(), 1e-6); - assertDoesNotThrow(solution::getMIPStats); - assertThrows(IllegalStateException.class, solution::getDualSolution); - solution.close(); - solution.close(); - } + try (Problem problem = new Problem("integer")) { + Variable x = problem.addVariable(0, 10, 1.0, VariableType.INTEGER, "x"); + problem.addConstraint(LinearExpression.of(x).ge(1.0)); + problem.setObjective(x, ObjectiveSense.MINIMIZE); + + try (SolverSettings settings = new SolverSettings().setSetting(CuOptConstants.CUOPT_TIME_LIMIT, 10.0); + Solution solution = problem.solve(settings)) { + assertTrue(solution.isMIP()); + assertEquals(TerminationStatus.OPTIMAL, solution.getTerminationStatus()); + assertEquals(1.0, x.getValue(), 1e-6); + assertDoesNotThrow(solution::getMIPStats); + assertThrows(IllegalStateException.class, solution::getDualSolution); + solution.close(); + solution.close(); + } + }🤖 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 `@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java` around lines 87 - 105, Wrap the Problem instance in try-with-resources in solvesProblemApiMIPAndLifecycleCloseIsIdempotent so it is closed like the other tests, and make the objective explicit instead of relying on the default sense. Use the existing Problem, SolverSettings, and solve flow to set the objective with the intended ObjectiveSense before calling solve, then keep the current MIP and close-idempotence assertions unchanged.
🧹 Nitpick comments (3)
java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemIntegrationTest.java (3)
31-47: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winEarly return for QP cases leaves two branches permanently dead.
verify()returns before callingcreateSettings/assertSolutionwheneverhasQuadraticObjective()is true. Since the only case exercisinghasQuadraticConstraint()(qp_diagonal_objective) also sets a quadratic objective, this means:
- the
else if (testCase.hasQuadraticObjective())branch increateSettings(Lines 147-148) is unreachable, and- the quadratic-constraint feasibility check in
CaseSpec.assertFeasible(Lines 707-729) is unreachable.As a result, quadratic-constraint satisfaction is only checked structurally (construction), never against actual solved primal values. Consider either removing the now-dead branches, or exercising them with a solvable QP+quadratic-constraint case (or a dedicated lower-iteration-limit-tolerant assertion) so this coverage isn't silently lost.
🤖 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 `@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemIntegrationTest.java` around lines 31 - 47, The early return in verify() for hasQuadraticObjective() makes the quadratic-objective branch in createSettings() and the quadratic-constraint feasibility path in CaseSpec.assertFeasible() unreachable. Update verify(), createSettings(), and/or CaseSpec so quadratic-constraint behavior is actually exercised by a solvable QP case, or remove the dead branches if they are no longer needed; use the existing symbols hasQuadraticObjective(), hasQuadraticConstraint(), createSettings(), and assertFeasible() to locate the affected logic.
162-180: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDuplicate
assumeNativeLibrary/assumeCudaDriverAvailableacross test classes.Both helpers are copy-pasted verbatim from
NativeIntegrationTest, and they've already drifted slightly: this copy addsredirectErrorStream(true)to thenvidia-smiprocess whileNativeIntegrationTest's copy doesn't. Consider extracting a shared test-support base class/utility to avoid future divergence.🤖 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 `@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemIntegrationTest.java` around lines 162 - 180, The helper methods assumeNativeLibrary and assumeCudaDriverAvailable are duplicated from NativeIntegrationTest and have already started to diverge, so extract them into a shared test-support base class or utility and update ProblemIntegrationTest to call the shared implementation. Keep the native library check and the CUDA driver probe in one place so both test classes use the same behavior, and ensure the nvidia-smi ProcessBuilder setup is centralized in the shared method.
612-618: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winConstraint-count formula assumes ranged rows always produce exactly 2 constraints.
linearProblemConstraintCount()returnsnumConstraints * 2for any ranged case, butcreateProblem()(Lines 499-505) only adds a lower/upper row when the corresponding bound is!Double.isInfinite(...). No current case has an actually-infinite ranged bound, so this passes today, but the formula and the construction logic will silently disagree the moment such a case is added. Separately,qp_diagonal_objective's upper bound is1.0e20— a large but finite double, notDouble.POSITIVE_INFINITY— so it's unclear whether that value was meant to represent "effectively unbounded" (in which case it should useDouble.POSITIVE_INFINITYto actually hit the skip branch) or a deliberately large finite bound.🔧 Suggested fix to make the count formula match the construction logic
- private int linearProblemConstraintCount() { - return isRanged() ? numConstraints * 2 : numConstraints; - } + private int linearProblemConstraintCount() { + if (!isRanged()) { + return numConstraints; + } + int count = 0; + for (int row = 0; row < numConstraints; row++) { + if (!Double.isInfinite(constraintLowerBounds[row])) { + count++; + } + if (!Double.isInfinite(constraintUpperBounds[row])) { + count++; + } + } + return count; + }🤖 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 `@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemIntegrationTest.java` around lines 612 - 618, The constraint-count logic in linearProblemConstraintCount() is assuming every ranged row always contributes two constraints, but createProblem() only adds each side when the bound is not infinite. Update the counting helper to mirror the actual construction in createProblem() by counting lower and upper contributions based on the same bound checks, and review the qp_diagonal_objective upper bound to make sure it intentionally uses a large finite value or is changed to a true unbounded value if that was the intent.
🤖 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
`@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java`:
- Around line 87-105: Wrap the Problem instance in try-with-resources in
solvesProblemApiMIPAndLifecycleCloseIsIdempotent so it is closed like the other
tests, and make the objective explicit instead of relying on the default sense.
Use the existing Problem, SolverSettings, and solve flow to set the objective
with the intended ObjectiveSense before calling solve, then keep the current MIP
and close-idempotence assertions unchanged.
---
Nitpick comments:
In
`@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemIntegrationTest.java`:
- Around line 31-47: The early return in verify() for hasQuadraticObjective()
makes the quadratic-objective branch in createSettings() and the
quadratic-constraint feasibility path in CaseSpec.assertFeasible() unreachable.
Update verify(), createSettings(), and/or CaseSpec so quadratic-constraint
behavior is actually exercised by a solvable QP case, or remove the dead
branches if they are no longer needed; use the existing symbols
hasQuadraticObjective(), hasQuadraticConstraint(), createSettings(), and
assertFeasible() to locate the affected logic.
- Around line 162-180: The helper methods assumeNativeLibrary and
assumeCudaDriverAvailable are duplicated from NativeIntegrationTest and have
already started to diverge, so extract them into a shared test-support base
class or utility and update ProblemIntegrationTest to call the shared
implementation. Keep the native library check and the CUDA driver probe in one
place so both test classes use the same behavior, and ensure the nvidia-smi
ProcessBuilder setup is centralized in the shared method.
- Around line 612-618: The constraint-count logic in
linearProblemConstraintCount() is assuming every ranged row always contributes
two constraints, but createProblem() only adds each side when the bound is not
infinite. Update the counting helper to mirror the actual construction in
createProblem() by counting lower and upper contributions based on the same
bound checks, and review the qp_diagonal_objective upper bound to make sure it
intentionally uses a large finite value or is changed to a true unbounded value
if that was the intent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 594299eb-4f80-4963-96fe-fd88c2b6acd4
📒 Files selected for processing (12)
.github/workflows/pr.yamldocs/cuopt/source/cuopt-java/convex/convex-api.rstdocs/cuopt/source/cuopt-java/convex/convex-examples.rstdocs/cuopt/source/cuopt-java/convex/index.rstdocs/cuopt/source/cuopt-java/quick-start.rstjava/cuopt/TESTS.mdjava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/NativeCuOpt.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/NativeProblem.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.javajava/cuopt/src/main/native/cuopt_jni.cppjava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.javajava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemIntegrationTest.java
💤 Files with no reviewable changes (3)
- docs/cuopt/source/cuopt-java/convex/convex-examples.rst
- java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/NativeCuOpt.java
- java/cuopt/src/main/native/cuopt_jni.cpp
✅ Files skipped from review due to trivial changes (2)
- java/cuopt/TESTS.md
- docs/cuopt/source/cuopt-java/quick-start.rst
🚧 Files skipped from review as they are similar to previous changes (2)
- docs/cuopt/source/cuopt-java/convex/index.rst
- .github/workflows/pr.yaml
|
🔔 Hi @anandhkb, 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. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java (1)
82-96: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Problemnative handle leaks if assertions fail insolvesProblemApiMIPAndLifecycleCloseIsIdempotent.
Problemis created outside try-with-resources (line 82), unlikesolvesSmallQPandwritesAndReadsMPSThroughReadAndParseMPSwhich correctly wrap it. IfaddVariable,addConstraint, or any assertion throws, the native handle is leaked. Per the review guide, resource management must be exception-safe.🔒 Proposed fix: wrap `Problem` in try-with-resources
- Problem problem = new Problem("integer"); - Variable x = problem.addVariable(0, 10, 1.0, VariableType.INTEGER, "x"); - problem.addConstraint(LinearExpression.of(x).ge(1.0)); - - try (SolverSettings settings = new SolverSettings().setSetting(CuOptConstants.CUOPT_TIME_LIMIT, 10.0); + try (Problem problem = new Problem("integer")) { + Variable x = problem.addVariable(0, 10, 1.0, VariableType.INTEGER, "x"); + problem.addConstraint(LinearExpression.of(x).ge(1.0)); + try (SolverSettings settings = new SolverSettings().setSetting(CuOptConstants.CUOPT_TIME_LIMIT, 10.0); Solution solution = problem.solve(settings)) { - assertTrue(solution.isMIP()); - assertEquals(TerminationStatus.OPTIMAL, solution.getTerminationStatus()); - assertEquals(1.0, x.getValue(), 1e-6); - assertDoesNotThrow(solution::getMIPStats); - assertThrows(IllegalStateException.class, solution::getDualSolution); - solution.close(); - solution.close(); + assertTrue(solution.isMIP()); + assertEquals(TerminationStatus.OPTIMAL, solution.getTerminationStatus()); + assertEquals(1.0, x.getValue(), 1e-6); + assertDoesNotThrow(solution::getMIPStats); + assertThrows(IllegalStateException.class, solution::getDualSolution); + solution.close(); + solution.close(); + } }🤖 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 `@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java` around lines 82 - 96, Update solvesProblemApiMIPAndLifecycleCloseIsIdempotent to create Problem within a try-with-resources block, enclosing variable creation, constraint setup, solving, and assertions so its native handle is closed on every exit path. Preserve the existing SolverSettings and Solution lifecycle checks.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeTestSupport.java`:
- Around line 22-31: Update assumeCudaDriverAvailable to bound process.waitFor
with a finite timeout, treating a timeout as an unavailable CUDA driver. Ensure
any unfinished nvidia-smi process is destroyed in a finally block, including
when InterruptedException occurs, while preserving the existing interrupt
restoration and assumption messages.
---
Outside diff comments:
In
`@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.java`:
- Around line 82-96: Update solvesProblemApiMIPAndLifecycleCloseIsIdempotent to
create Problem within a try-with-resources block, enclosing variable creation,
constraint setup, solving, and assertions so its native handle is closed on
every exit path. Preserve the existing SolverSettings and Solution lifecycle
checks.
🪄 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: a7e7ea71-4d49-48c3-86a1-c76715ab0c90
📒 Files selected for processing (15)
.github/workflows/build.yamljava/cuopt/CMakeLists.txtjava/cuopt/scripts/build_native.shjava/cuopt/scripts/java_home.shjava/cuopt/scripts/test.shjava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/CSRMatrix.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/LinearExpression.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/NativeProblem.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.javajava/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/QuadraticExpression.javajava/cuopt/src/main/native/cuopt_jni.cppjava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeIntegrationTest.javajava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeTestSupport.javajava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemIntegrationTest.javajava/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemModelingTest.java
🚧 Files skipped from review as they are similar to previous changes (8)
- java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/CSRMatrix.java
- .github/workflows/build.yaml
- java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/ProblemIntegrationTest.java
- java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/LinearExpression.java
- java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/NativeProblem.java
- java/cuopt/src/main/native/cuopt_jni.cpp
- java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/QuadraticExpression.java
- java/cuopt/src/main/java/com/nvidia/cuopt/mathematicalprogramming/Problem.java
| static void assumeCudaDriverAvailable() { | ||
| try { | ||
| Process process = new ProcessBuilder("nvidia-smi").redirectErrorStream(true).start(); | ||
| Assumptions.assumeTrue(process.waitFor() == 0, "CUDA driver is unavailable"); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| Assumptions.assumeTrue(false, "CUDA driver check was interrupted"); | ||
| } catch (Exception e) { | ||
| Assumptions.assumeTrue(false, "CUDA driver check failed: " + e.getMessage()); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
sed -n '1,240p' java/cuopt/pom.xml \
| grep -nE 'maven.compiler|<release>|<source>|<target>|java.version' || true
rg -n -C 4 'assumeCudaDriverAvailable|waitFor\(' \
java/cuopt/src/test/javaRepository: NVIDIA/cuopt
Length of output: 8033
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
cat -n java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeTestSupport.javaRepository: NVIDIA/cuopt
Length of output: 1602
Bound the CUDA-driver probe and clean up the subprocess.
process.waitFor() can block this test job indefinitely if nvidia-smi hangs, and the interrupted path leaves the spawned process running. Add a timeout and destroy any unfinished process in finally.
🤖 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
`@java/cuopt/src/test/java/com/nvidia/cuopt/mathematicalprogramming/NativeTestSupport.java`
around lines 22 - 31, Update assumeCudaDriverAvailable to bound process.waitFor
with a finite timeout, treating a timeout as an unavailable CUDA driver. Ensure
any unfinished nvidia-smi process is destroyed in a finally block, including
when InterruptedException occurs, while preserving the existing interrupt
restoration and assumption messages.
nvidiacbrissette
left a comment
There was a problem hiding this comment.
Latest version deals with all of these except for adding to Maven, and adding functionality to the C API.
| =================================== | ||
|
|
||
| The Java LP/QP bindings are in the package | ||
| ``com.nvidia.cuopt.linearprogramming``. The public API is documented below by |
| High-level model | ||
| ---------------- | ||
|
|
||
| ``Problem`` is the recommended entry point for models built in Java. |
There was a problem hiding this comment.
Changed this to problem throughout.
| - Set a quadratic objective with optional linear and constant terms. | ||
| * - ``solve()`` / ``solve(SolverSettings)`` | ||
| - Convert the model to a native ``DataModel`` and return a ``Solution``. | ||
| * - ``toDataModel()`` |
There was a problem hiding this comment.
Fully depreciated in latest.
|
|
||
| The model also exposes ``getVariables``, ``getVariable``, ``getConstraints``, | ||
| ``getConstraint``, ``getNumVariables``, ``getNumConstraints``, | ||
| ``getNumNonZeros``, ``isMip``, ``isSolved``, ``getStatus``, |
There was a problem hiding this comment.
Changed capitalization throughout.
|
|
||
| ``SolverSettings`` owns native solver configuration and implements | ||
| ``AutoCloseable``. Parameters can be set with the overloaded | ||
| ``setParameter`` methods for ``String``, ``int``, ``double``, and ``boolean`` |
| package com.nvidia.cuopt.linearprogramming; | ||
|
|
||
| public enum TerminationStatus { | ||
| NO_TERMINATION(0), |
There was a problem hiding this comment.
Again pulling from C++ public header.
| @@ -0,0 +1,34 @@ | |||
| com/nvidia/cuopt/linearprogramming/SolverSettings$NativeHandle.class | |||
There was a problem hiding this comment.
These should now be excluded.
|
|
||
| <groupId>com.nvidia</groupId> | ||
| <artifactId>cuopt</artifactId> | ||
| <version>26.8.0-SNAPSHOT</version> |
There was a problem hiding this comment.
Changed the version format, added an update marker, and groupId added. Additionally added ci/release/update-version.sh.
| @@ -0,0 +1,42 @@ | |||
| ==================================== | |||
Description
Added Java bindings at parity with LP/MIP/QP along with some basic tests for functionality. Currently it is fully decoupled from other build components and must be built on its own.
Checklist