Skip to content

java: cuopt-java initial release — LP/MILP/QP API + classifier JARs#1192

Draft
rgsl888prabhu wants to merge 22 commits into
NVIDIA:mainfrom
rgsl888prabhu:feat/java-api-skeleton
Draft

java: cuopt-java initial release — LP/MILP/QP API + classifier JARs#1192
rgsl888prabhu wants to merge 22 commits into
NVIDIA:mainfrom
rgsl888prabhu:feat/java-api-skeleton

Conversation

@rgsl888prabhu
Copy link
Copy Markdown
Collaborator

@rgsl888prabhu rgsl888prabhu commented May 8, 2026

Summary

Initial Java interface for cuOpt covering LP, MILP, and QP (beta). Models cuvs-java's architecture (Project Panama / FFM, Java 21 base API + Java 22 multi-release JAR for FFM impl). API surface follows Gurobi-style conventions for problem construction and Python cuOpt conventions for solution access.

For reviewers — what to skip

84% of this diff (~23,812 of ~28,262 lines) is auto-generated. The directory java/cuopt-java/src/main/java22/com/nvidia/cuopt/internal/panama/ is jextract output from cpp/include/cuopt/linear_programming/cuopt_c.h. The single file cuopt_c_h.java is 23,515 lines. Skip the entire panama/ subdirectory — the drift gate in java/build.sh keeps it in sync with the C header on every build.

The remaining ~4,350 hand-written lines, in suggested review order:

Area Files Lines Biggest file
Public API (Java 21, src/main/java/) 23 2,105 Problem.java (421)
FFM impl (Java 22, src/main/java22/.../internal/, excl. panama) 4 902 CuOptProviderImpl.java (531)
Maven + build scripts (pom.xml, build.sh, generate-bindings.sh, assembly) 5 625 pom.xml (317)
Tests (src/test/java/) 5 315
CI / conda / dependencies (pr.yaml, build.yaml, dependencies.yaml, env files) 9 248 pr.yaml (~69)
READMEs / resources / .gitignore 8 156

What's in this PR

Public API (Java 21, src/main/java/com/nvidia/cuopt/)

  • Problem, Variable, Constraint, LinearExpr, QuadraticExpr, SolverSettings, DataModel
  • Enums: VType, CType, Sense, SolverMethod, PdlpSolverMode, TerminationStatus, ErrorStatus, ProblemCategory
  • Records: LpStats, MilpStats
  • @FunctionalInterface callbacks: MIPGetSolutionCallback, MIPSetSolutionCallback
  • CuOpt constants holder, Solver entry points, CuOptException
  • spi/CuOptProvider (sealed) — Layer 4 bridge between Java 21 public API and Java 22 FFM impl

FFM implementation (Java 22, src/main/java22/)

  • CuOptProviderImpl — full impl: settings, parameter setters, solveProblem, solveDataModel
  • CsrBuilder — translates List<LinearExpr> to CSR + Q-matrix CSR
  • NativeLibraryLoader — chooses between embedded (classifier JAR) and BYO (System.loadLibrary) modes via JAR manifest
  • internal/panama/ — jextract bindings (committed, drift-gate-stable)

Build / CI

  • java/build.sh orchestrates regen bindings → drift gate → mvn verify
  • panama-bindings/generate-bindings.sh auto-downloads jextract per arch on first run; idempotently normalizes output
  • ci/build_java.sh + ci/test_java.sh with --run-java-tests / --unit-tests-only modes
  • pr.yaml: conda-java-build-and-tests (amd64 GPU IT) + conda-java-build-and-tests-arm64 (arm64 GPU IT)
  • build.yaml: java-build + java-build-arm64 for main/release/tag pushes

Distribution

  • Base JAR (cuopt-java-<version>.jar) — always built. BYO libcuopt.so on java.library.path (typical conda).
  • Classifier JAR (cuopt-java-<version>-<arch>-cuda<n>.jar) — opt-in via mvn -Dcuda.version=<n> or CLASSIFIER_CUDA=<n> ./java/build.sh. Bundles libcuopt.so, libmps_parser.so, librmm.so, librapids_logger.so under <arch>/Linux/, with manifest entry Embedded-Libraries-Cuda-Version=<n>. User still installs CUDA toolkit matching <n>.

MIP user callbacks

  • SolverSettings.setMIPGetSolutionCallback(cb) — user-provided callback invoked when the solver finds a new incumbent. Reads the user's lambda at every native call so the latest registration wins.
  • SolverSettings.setMIPSetSolutionCallback(cb) — user injects a candidate solution. Registering this disables presolve (per the C contract).
  • FFM upcall stubs are bound to the per-solve Arena and re-created on every solve, so cross-solve reuse of the same SolverSettings is safe. Trampolines catch Throwable so user-code exceptions never propagate across the native boundary.

Tests

  • Unit: LinearExprTest (4)
  • Integration: SolverIT, LpSolverIT, MilpSolverIT, QpSolverIT, MipCallbackIT (8 total)
  • All green locally (~4s)

Design inputs

Out of scope — tracked as follow-up issues

# Item Reason
#1202 C API stats getters (LP residuals, iterations, gap, method; MIP nodes/simplex iters/presolve time/violations) Python sees these via Cython→C++ direct; the C ABI doesn't expose them. Currently NaN / -1 / UNSET in LpStats/MilpStats. Cross-team C++ work.
#1203 Static-link gRPC, protobuf, abseil, TBB, libgomp into libcuopt.so Removes runtime system-package friction for non-conda users. CMake-side change.

Test plan

  • ./java/build.sh passes locally (drift gate stable, 4 unit + 6 IT)
  • CLASSIFIER_CUDA=13 ./java/build.sh produces a classifier JAR; embedded mode verified by running with empty LD_LIBRARY_PATH
  • CI green on amd64 (conda-java-build-and-tests)
  • CI green on arm64 GPU (conda-java-build-and-tests-arm64)
  • CodeRabbit / coderabbit-review feedback addressed

Adds the initial directory structure for a Java interface to cuOpt
under java/, modeled after cuvs-java's architecture (Project Panama /
FFM, Java 21 base API + Java 22 multi-release JAR for FFM impl).

The first end-to-end demo is Solver.getVersion(), which exercises the
full FFM bridge: Java 21 public API -> ServiceLoader SPI -> Java 22
FFM implementation -> jextract panama bindings -> libcuopt.so. Full
LP/MILP/QP support lands in subsequent PRs.

Build setup encodes three lessons from cuvs-java:
- Java 21 base API + Java 22 multi-release JAR for FFM (cuvs Issue NVIDIA#1066)
- maven-compiler-plugin 3.11.0+ with serial phase bindings (#1293)
- Spotless under <plugins> only when added (NVIDIA#1090)

Per-folder READMEs explain the role of each layer in the architecture.

Adds build_java group and test_java file definition to dependencies.yaml
so conda-forge openjdk + maven dependencies flow into developer envs.

Toolchain prerequisites for local build (not auto-installed):
- conda install -c conda-forge openjdk=22 maven
- jextract from https://jdk.java.net/jextract/
@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented May 8, 2026

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

Builds on the prior commit by:

1. Renaming the dependency group `build_java` -> `java` to match cuvs
   convention, and adding it to the `all` file's includes so all
   conda/environments/all_cuda-*_arch-*.yaml files now ship with
   openjdk=22.* and maven>=3.9.6.

2. Adding ci/build_java.sh and ci/test_java.sh, mirroring cuvs's
   pattern. ci/test_java.sh is a thin wrapper that delegates to
   ci/build_java.sh --run-java-tests. Splitting build/test into
   separate jobs is deferred (matches cuvs Issue NVIDIA#868) — requires
   shared-workflow surface for cross-job artifact passing.

3. Adding .github/workflows/pr.yaml jobs:
   - test_java paths group in changed-files
   - conda-java-build-and-tests-matrix (compute-matrix; amd64-only)
   - conda-java-build-and-tests (custom-job; gpu-l4-latest-1)
   - Adds conda-java-build-and-tests to pr-builder.needs

4. Adding java-build job to .github/workflows/build.yaml so main /
   release-branch / tag pushes produce cuopt-java JAR artifacts.

CI will fail on this PR until two follow-up steps:
- jextract is added to the rapidsai/ci-conda CI image (or pulled in
  ci/build_java.sh)
- Generated panama bindings under
  java/cuopt-java/src/main/java22/com/nvidia/cuopt/internal/panama/
  are committed (initial bootstrap; runs on a workstation with JDK 22 +
  jextract installed locally)
Builds out the full public Java API for LP, MILP, and QP on top of the
skeleton + CI from the prior commits.

Public API surface (Java 21, src/main/java/):
- Problem: build problem (addVariable, addConstraint, setObjective),
  solve(), and post-solve accessors (status, objectiveValue, solveTime,
  lpStats, milpStats). Owns native handle + all solution arrays.
- Variable / Constraint: immutable handles that delegate value() /
  reducedCost() / dualValue() / slack() back to the owning Problem.
- LinearExpr / QuadraticExpr: mutable expression builders supporting
  both addTerm(coeff, var) (chainable single-term) and
  addTerms(double[], Variable[]) (bulk). Coefficients accumulate.
- SolverSettings: typed setters (setTimeLimit, setOptimalityTolerance,
  setMethod, setRelativeMipGap, ...) + generic setParameter(name, value)
  escape hatch for forward compatibility. AutoCloseable.
- DataModel: low-level escape hatch matching cuOpt Python's DataModel.
  CSR setters return this for chaining.
- Enums: VType (CONTINUOUS/INTEGER/BINARY), CType (LE/GE/EQ), Sense,
  SolverMethod, PdlpSolverMode, TerminationStatus, ErrorStatus,
  ProblemCategory.
- Records: LpStats, MilpStats.
- CuOpt constants holder: static-importable INF, MINIMIZE, LESS_EQUAL,
  and parameter-name string constants (TIME_LIMIT, METHOD, etc.).

FFM implementation (Java 22, src/main/java22/internal/):
- CuOptProviderImpl: marshals Java arrays -> MemorySegment, calls
  jextract-generated cuopt_c_h methods, extracts solution data into
  Java arrays before freeing the native handle.
- CsrBuilder: translates List<LinearExpr> constraint expressions into
  CSR (row-offsets, column-indices, values) sorted by column.
- Native handles cross the SPI boundary as long (raw addresses);
  ProblemImpl reconstructs MemorySegment from the address on each call.

Tests:
- LpSolverTest: simple LP, diet LP, introspection.
- MilpSolverTest: 0-1 knapsack.
- QpSolverTest: mirrors cuOpt Python test_qp.py::test_solver.
- LinearExprTest: addTerm/addTerms interchangeability, coefficient
  accumulation, length-mismatch and mixed-problem error paths.

This commit completes the full LP/MILP/QP public API. Tests cannot run
until: (1) JDK 22 + jextract installed on a workstation, (2) generated
panama bindings committed under
java/cuopt-java/src/main/java22/com/nvidia/cuopt/internal/panama/. See
temp/cuopt-java-RESUME.md for the verify-locally checklist.
@rgsl888prabhu rgsl888prabhu changed the title java: add cuopt-java skeleton with FFM bridge demo java: cuopt-java skeleton + CI + LP/MILP/QP public API May 11, 2026
jextract for JDK 22 is not on conda-forge. Match cuvs's approach of
auto-downloading it into java/panama-bindings/jextract-22/ on first
run of generate-bindings.sh.

- panama-bindings/generate-bindings.sh now downloads
  openjdk-22-jextract+6-47 from download.java.net if jextract isn't
  already on PATH. Subsequent builds reuse the local copy.
- panama-bindings/.gitignore excludes jextract-22/ and the .tar.gz
  download artifact from git.
- build.sh drops the hard error on missing jextract; the auto-download
  in generate-bindings.sh handles it.
- README and dependencies.yaml comments updated to reflect the
  auto-download.

CI implication: the first 'ci/build_java.sh' run will spend ~10
seconds downloading jextract (62 MB). Subsequent runs cache it in
the conda env's workspace if persistent, or re-download otherwise.
A follow-up could preinstall jextract in the rapidsai/ci-conda image
to skip the download in CI.
Five fixes to get the build green end-to-end:

1. jextract: pass --header-class-name cuopt_c_h so the generated header
   class is named what CuOptProviderImpl imports (default would be
   headers_h after the umbrella include filename). Also add
   --use-system-load-library so dlopen uses java.library.path.

2. Make package-private accessors PUBLIC across the
   internal/-vs-linear_programming/ package boundary:
   - Problem: linearObjective, quadraticObjective, objectiveSense,
     objectiveOffset, constraintExpressions
   - QuadraticExpr: quadVar1, quadVar2, quadCoeff
   - SolverSettings: nativeHandle
   - DataModel: all ~15 internal getters
   Java's package-visibility doesn't cross the package boundary the
   FFM impl needs to traverse. Marked with javadoc that these are
   for internal FFM use only.

3. Multi-Release JAR semantics only apply to classes loaded from an
   actual JAR file, NOT exploded target/classes/. So tests that touch
   the FFM impl can't run via maven-surefire-plugin against
   target/classes/. Moved them to *IT.java (failsafe convention) and
   configured maven-failsafe-plugin with classesDirectory pointing at
   the packaged JAR. Failsafe runs after package, so the JAR exists.

   LinearExprTest stays as a surefire test (pure Java, no FFM impl).
   SolverIT / LpSolverIT / MilpSolverIT / QpSolverIT run via failsafe.

4. build.sh: replace CMAKE_PREFIX_PATH with CUOPT_LIB_DIR. The previous
   default fell through to the user's conda env path (set by conda
   activate), missing the actual libcuopt.so in cpp/build/. Now we
   probe cpp/build/ and $CONDA_PREFIX/lib in that order.

5. Commit the jextract-generated panama bindings (cuopt_c_h.java,
   __fsid_t.java, cuOptMIPGetSolutionCallback.java,
   cuOptMIPSetSolutionCallback.java). These regenerate on every build
   and the drift gate enforces consistency from this commit forward.

Verified locally: full pipeline runs clean with
  ./java/build.sh    (no SKIP_DRIFT_CHECK needed after this commit)

Test results:
  Surefire:  4/4 LinearExprTest pass
  Failsafe:  6/6 integration tests pass against libcuopt.so
             (LP, MILP, QP all solve to feasibility/optimality)
Plumbs arm64 through the build and CI pipelines:

- supported-platforms.properties: flip linux-aarch64=true.
- generate-bindings.sh: detect uname -m and select matching jextract
  tarball (linux-aarch64 confirmed available on download.java.net)
  and CUDA include subdir (targets/aarch64-linux/include).
- java/build.sh + ci/build_java.sh: add UNIT_TESTS_ONLY mode for the
  arm64 CPU runner, which has no GPU and so must skip integration
  tests.
- ci/build_java.sh: drop the stale fail-fast jextract-availability
  check; auto-download in generate-bindings.sh now handles it.
- pr.yaml: add conda-java-build-arm64-matrix + conda-java-build-arm64
  jobs on linux-arm64-cpu16, building the JAR and running unit tests.
  amd64 keeps full IT on gpu-l4-latest-1.
- build.yaml: add java-build-arm64 sibling job for main/release/tag.

IT stays on amd64 until an arm64 GPU runner is wired into
rapidsai/shared-workflows; flip --unit-tests-only to --run-java-tests
and update the node_type when that lands.
The rapidsai/shared-workflows matrix already includes arm64 GPU rows
(linux-arm64-gpu-l4-latest-1) used by conda-python-tests, so the
Java arm64 leg no longer needs to be CPU-only. Rename the job to
match the amd64 sibling and switch to the full IT script.
Adds a Maven `classifier-jar` profile that produces
cuopt-java-<version>-<arch>-cuda<n>.jar bundling libcuopt.so,
libmps_parser.so, librmm.so, and librapids_logger.so under
<arch>/Linux/ inside the JAR, with manifest entry
Embedded-Libraries-Cuda-Version=<n>. Activated by
`mvn -Dcuda.version=<n>` or `CLASSIFIER_CUDA=<n> ./java/build.sh`.
The base JAR (always built) is unchanged: BYO libcuopt via
System.loadLibrary.

NativeLibraryLoader now inspects the JAR manifest. If the embedded
marker is present, it extracts each lib to a temp file and
System.load-s them in dependency order (rapids_logger → rmm →
mps_parser → cuopt). Otherwise it falls back to System.loadLibrary
("cuopt").

The jextract-generated System.loadLibrary in cuopt_c_h's static
initializer is rewritten by generate-bindings.sh to call
NativeLibraryLoader.ensureLoaded, so both distribution modes are
routed through the new loader.

Not bundled (size/license/ABI risk; static-linking follow-up tracked
in NVIDIA#1203): CUDA toolkit, gRPC, protobuf, abseil, TBB, libgomp,
libstdc++.

Local verification:
- Base build: mvn clean verify passes (4 unit + 6 IT)
- Classifier build: CLASSIFIER_CUDA=13 produces a 56 MB JAR;
  manual run with empty LD_LIBRARY_PATH loads cuopt via embedded mode.
- Drift gate: regen is idempotent.
@rgsl888prabhu rgsl888prabhu changed the title java: cuopt-java skeleton + CI + LP/MILP/QP public API java: cuopt-java initial release — LP/MILP/QP API + classifier JARs May 12, 2026
Adds two @FunctionalInterface types in the public API:

  MIPGetSolutionCallback.onSolution(double[] solution,
                                    double objectiveValue,
                                    double solutionBound)

  MIPSetSolutionCallback.provideSolution(double[] outSolution,
                                         double[] outObjective,
                                         double solutionBound)

Registered via SolverSettings.setMIPGetSolutionCallback /
setMIPSetSolutionCallback, both with null-clear semantics.

Implementation in CuOptProviderImpl.registerMipCallbacks creates
per-solve Arena-scoped FFM upcall stubs using the jextract-emitted
allocate(Function, Arena) helpers on the cuOptMIP{Get,Set}SolutionCallback
binding classes. The Get trampoline is always registered when settings
is non-null and reads the user's lambda at call time (no-ops if null) —
this is safe because Get registration has no solver side effects. The
Set trampoline is only registered when the user has actually provided
one, because registering it disables presolve per the C contract.

Trampolines catch Throwable internally; exceptions never propagate
across the FFM boundary.

Includes MipCallbackIT with two cases: Get callback observes at least
one incumbent and its primal[] is sized numVars; Set callback is
invoked at least once.
Gates conda-cpp-tests, conda-python-build, conda-python-tests,
docs-build, all wheel-build/wheel-tests jobs, and
test-self-hosted-server with `if: false`, and drops them from
`pr-builder.needs`. Lanes kept on:

  - pre-flight: check-lean-ci, prevent-merge-with-lean-ci,
    compute-matrix-filters, changed-files, checks
  - conda-cpp-build
  - conda-java-build-and-tests (amd64 GPU IT)
  - conda-java-build-and-tests-arm64 (arm64 GPU IT)

Revert this commit before merging PR NVIDIA#1192.
@rgsl888prabhu rgsl888prabhu self-assigned this May 12, 2026
@rgsl888prabhu rgsl888prabhu added feature request New feature or request non-breaking Introduces a non-breaking change labels May 12, 2026
@rgsl888prabhu
Copy link
Copy Markdown
Collaborator Author

/ok to test 6283b6f

…encies)

The shared rapidsai check `rapids-check-pr-job-dependencies` requires
every top-level job in pr.yaml to be listed in `pr-builder.needs` (or
in `ignored_pr_jobs`). The prior [TEMP] commit shrank that list to
just the lanes we want CI signal on, which tripped the check and
cascade-cancelled all downstream jobs.

Restore the full list. pr-builder.yaml treats skipped jobs as
success, so the `if: false` gates still effectively skip the
non-Java lanes — they just have to be listed by name.

Also adds the previously-missing `conda-java-build-and-tests-matrix`
and `conda-java-build-and-tests-arm64-matrix` helper jobs to the
list (they were never in `needs` since the Java lanes were added).

Follow-up to 6283b6f. Both this and 6283b6f are temporary and
should be reverted before merge.
…e non-Java PR lanes"

Restores the original PR CI workflow: all lanes (cpp, java, python,
docs, wheels, self-hosted server) run on each PR push. The two
prior commits gated non-Java lanes off with `if: false` to speed up
feedback during cuopt-java development; with the feature work
landed, we want full CI signal again before merge.

Reverts: eb91e7e (ci: list all jobs in pr-builder.needs ...)
Reverts: 6283b6f ([TEMP] ci: disable non-Java PR lanes ...)
rapids-check-pr-job-dependencies (run as part of the shared `checks`
workflow) requires every top-level job in pr.yaml to appear in
`pr-builder.needs` (or in `ignored_pr_jobs`). The two Java matrix-
compute helper jobs were missing from `pr-builder.needs` ever since
they were introduced — pre-existing gap, surfaced now that CI is
running on this PR.

pr-builder treats skipped or success as passing, so adding these
intermediate compute jobs to the list has no behavioral effect on
green PRs.
@rgsl888prabhu
Copy link
Copy Markdown
Collaborator Author

/ok to test 2e3d2fe

rgsl888prabhu and others added 3 commits May 12, 2026 15:11
The conda env build was failing with:

  ValueError: No matching matrix found in 'cuda_version' for: {'cuda': '13', 'arch': 'x86_64'}

dependencies.yaml's `cuda_version` matrix is keyed on major.minor
(e.g. "13.0", "13.1"); we were stripping RAPIDS_CUDA_VERSION to just
the major ("13") via `%%.*` (longest prefix-match-from-end). The
result didn't match any matrix row, the dep-file-generator errored,
the conda env was created empty, and the subsequent build failed
with "java not found in PATH".

Switch to `%.*` (shortest prefix-match-from-end) to keep "13.0" from
"13.0.2", matching what ci/test_python.sh:23 does for the same env
variable.
Removes the following PR-only jobs entirely (not just `if: false`-d):

  - conda-cpp-tests
  - conda-python-build
  - conda-python-tests
  - docs-build
  - wheel-build-cuopt-mps-parser
  - wheel-build-libcuopt
  - wheel-build-cuopt
  - wheel-tests-cuopt
  - wheel-build-cuopt-server
  - wheel-build-cuopt-sh-client
  - wheel-tests-cuopt-server
  - test-self-hosted-server

And drops their references from `pr-builder.needs`.

Kept on:

  - pre-flight (check-lean-ci, prevent-merge-with-lean-ci,
    compute-matrix-filters, changed-files, checks)
  - conda-cpp-build
  - conda-java-build-and-tests* (amd64 GPU IT)
  - conda-java-build-and-tests-arm64* (arm64 GPU IT)

Earlier `if: false` approach broke because the shared
`rapids-check-pr-job-dependencies` check requires every job to be
listed in pr-builder.needs. Removing the jobs entirely sidesteps
that. **Revert this commit before merge.**
The Java jobs were added before zizmor was wired into pre-commit
(upstream PR NVIDIA#1181). They use `secrets: inherit` for the same reason
all other reusable-workflow consumers in this repo do — the called
workflow needs the script-env secrets to upload artifacts. Add the
suppression comment used by the rest of the file.
@rgsl888prabhu
Copy link
Copy Markdown
Collaborator Author

/ok to test 0aff867

Pre-existing gap on the Java jobs from my earlier commits, surfaced
by the upstream `permissions: {}` top-level default added in
PR NVIDIA#1181: with no top-level permissions and no per-job block, each
Java reusable-workflow call inherited the deny-all default and
GitHub Actions rejected the workflow with

  is requesting 'actions: read, contents: read, ...', but is only
  allowed 'actions: none, contents: none, ...'

Mirror the permissions block that `conda-cpp-build` uses (actions,
contents, packages, pull-requests, id-token) on the two Java
build+test jobs and the two compute-matrix helpers.
@rgsl888prabhu
Copy link
Copy Markdown
Collaborator Author

/ok to test 8259ac5

Same pattern as 0aff867 (which fixed it on pr.yaml). The two
java-build jobs in build.yaml were also added before zizmor was
wired into pre-commit (upstream PR NVIDIA#1181). Add the suppression
comment used by every other reusable-workflow consumer in this file.
@rgsl888prabhu
Copy link
Copy Markdown
Collaborator Author

/ok to test 9689442

CI was failing on the jextract regen step with:

  ERROR: Could not locate a CUDA include directory.

generate-bindings.sh needs CUDA dev headers to run jextract against
cuopt_c.h's transitive #includes. The conda env used in CI for the
Java jobs (`test_java` group in dependencies.yaml) only installs
openjdk + maven + libcuopt — no CUDA dev headers.

CI was already skipping the drift check (SKIP_DRIFT_CHECK=true), so
the regen was wasted work and just a fragile failure point. Add a
SKIP_BINDINGS_REGEN env var and set it in ci/build_java.sh — the
committed bindings are trusted, with the dev-workstation drift gate
(./java/build.sh without flags) as the authoritative check.

Verified locally: with SKIP_BINDINGS_REGEN=true the regen step is
skipped and the build still succeeds.
@rgsl888prabhu
Copy link
Copy Markdown
Collaborator Author

/ok to test 54a8b9c

The Java CI conda env was missing libcusparse.so.12 (and friends) at
load time:

  java.lang.UnsatisfiedLinkError: /opt/conda/envs/java/lib/libcuopt.so:
    libcusparse.so.12: cannot open shared object file: No such file or
    directory

libcuopt's conda recipe lists libcublas / libcudss-dev / libcusparse-dev
only under `host:` (build-time) and explicitly `ignore_run_exports`-es
them; CUDA runtime libs are not declared as `run:` deps. Python tests
work around this by pulling them in transitively via cudf / cupy /
cuda-python. The Java env has no such chain, so we have to declare
them ourselves.

Adds libcublas, libcudart, libcudss, libcusparse, libnvjitlink to the
`java` dep group in dependencies.yaml. Mirrors the ldd of libcuopt.so
(libcublasLt rides along with libcublas).

A cleaner fix would be to add these to libcuopt's `run:` deps, but
that's a broader change with cross-team coordination.
@rgsl888prabhu
Copy link
Copy Markdown
Collaborator Author

/ok to test f27ed56

}

/** Whether this is a ranged constraint (lower &lt;= lhs &lt;= upper). */
public boolean isRanged() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's remove isRanged(). Constraints should not be ranged.

* Lower bound for ranged constraints. For one-sided constraints,
* returns the appropriate one-sided bound (e.g. rhs for GE; -infinity for LE).
*/
public double lowerBound() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remove lowerBound and upperBound. Constraints should just have a rhs

* }
* }</pre>
*/
public final class DataModel implements AutoCloseable {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we need DataModel

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The user should create problems directly with the Problem


// LinkedHashMap preserves insertion order (useful for deterministic
// CSR builds), keys are Variables, values are summed coefficients.
private final Map<Variable, Double> terms = new LinkedHashMap<>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's discuss implementation. I don't think we want to use HashMaps here.

* @param presolveTime wall-clock time spent in presolve, seconds
* @param rootRelaxationTime wall-clock time spent on the root LP relaxation, seconds
*/
public record MilpStats(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should use MIP instead of MILP. Capitalization should be consistent so MIPStats

private final List<Variable> variables = new ArrayList<>();
private final List<Constraint> constraints = new ArrayList<>();
private final List<LinearExpr> constraintExpressions = new ArrayList<>();
private final Map<String, Variable> variablesByName = new HashMap<>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's discuss and possible remove Map here.

return n;
}

public boolean isMip() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent capitalization of abbrevation isMIP

return false;
}

public boolean isQp() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent capitalization isQP

/** Linear program: linear objective, linear constraints, all continuous variables. */
LP,
/** Integer program: linear objective and constraints, all variables integer. */
IP,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remove IP.

* {@code src/main/java22/}; the JVM resolves it via
* {@link java.util.ServiceLoader} on Java 22+ runtimes.
*/
public interface CuOptProvider {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent capitalization: cuOptProvider

* grouped at the bottom. They're used with
* {@link com.nvidia.cuopt.linear_programming.SolverSettings#setParameter(String, Object)}.
*/
public final class CuOpt {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent capitalization cuOpt

* the runtime environment is misconfigured (e.g., the native library
* cannot be loaded, or the JVM is older than Java 22).
*/
public class CuOptException extends RuntimeException {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent captialization: cuOpt

*
* <p>Output arrays are ready to copy into native {@code MemorySegment}s.
*/
final class CsrBuilder {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent capitalization: CSRBuilder

* addresses); this class reconstructs {@code MemorySegment} from the
* addresses on each call.
*/
public final class CuOptProviderImpl implements CuOptProvider {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent capitalization: cuOptProviderImpl


problem.solve(settings);

assertTrue(problem.isMip());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent capitalization: isMIP

import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;

class MipCallbackIT {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent capitalization: MIPCallbackIT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why does it have an IT at the end

* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.nvidia.cuopt.linear_programming;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we should call this com.nvidia.cuopt.linear_programming. We should try to deprecate the linear_programming term since cuOpt contains much more than this now.

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants