Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9e8fba9
Java bindings for LP/MIP/QP
nvidiacbrissette Jul 7, 2026
9e76c16
removing some logs etc.
nvidiacbrissette Jul 7, 2026
c61bb62
many PR updates based on comments.
nvidiacbrissette Jul 9, 2026
e8f178d
Fully removed DataModel
nvidiacbrissette Jul 9, 2026
85e5440
Reorganized pr.yaml to avoid conflicts.
nvidiacbrissette Jul 9, 2026
fa275f6
Changes in response to comments.
nvidiacbrissette Jul 17, 2026
54bfe2d
style and test updates.
nvidiacbrissette Jul 29, 2026
1f5a5fb
Merge branch 'main' into cbrissette/cuopt-bindings
ramakrishnap-nv Aug 10, 2026
275f657
Move the Java native extensions into the public C API
ramakrishnap-nv Aug 11, 2026
233a9c1
Add PDLP warm-start settings and harden the CUDA driver probe
ramakrishnap-nv Aug 11, 2026
7063df8
Route problem bound getters through the C API
ramakrishnap-nv Aug 11, 2026
93c907a
Keep the LP solution getters on the internal path
ramakrishnap-nv Aug 11, 2026
9decf1e
Wire the Java module into build.sh and align it with cuvs
ramakrishnap-nv Aug 11, 2026
d2ceef7
Guard against JNI symbol drift and complete the POM metadata
ramakrishnap-nv Aug 11, 2026
f0a264f
Add an escape hatch to the JNI symbol check
ramakrishnap-nv Aug 11, 2026
57aaeb6
Address CodeRabbit review on the pushed changes
ramakrishnap-nv Aug 12, 2026
d87382c
Fix pre-commit failures
ramakrishnap-nv Aug 12, 2026
4db2d2b
Merge branch 'main' into cbrissette/cuopt-bindings
ramakrishnap-nv Aug 12, 2026
bbe4b5b
Merge branch 'main' into cbrissette/cuopt-bindings
ramakrishnap-nv Aug 12, 2026
99c3964
Expose solver statistics as solution attributes
ramakrishnap-nv Aug 12, 2026
5972293
Align the solution attribute constants
ramakrishnap-nv Aug 12, 2026
4eb70d8
Record why the parameter entry points were dropped, not deferred
ramakrishnap-nv Aug 12, 2026
d981aec
Sync the C API review changes from #1715
ramakrishnap-nv Aug 13, 2026
6a04604
Sync the attribute dispatch change from #1715
ramakrishnap-nv Aug 13, 2026
8d854d3
Merge remote-tracking branch 'origin/main' into pr-1524-java
ramakrishnap-nv Aug 14, 2026
dc8b4b0
Address the self-contained review comments on the Java surface
ramakrishnap-nv Aug 14, 2026
39cbff3
Remove the IP paragraph from the Java docs
ramakrishnap-nv Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,27 @@ jobs:
date: ${{ inputs.date }}
sha: ${{ inputs.sha }}
script: ci/build_cpp.sh
java-build:
needs: cpp-build
permissions:
actions: read
contents: read
id-token: write
packages: read
pull-requests: read
secrets: inherit # zizmor: ignore[secrets-inherit]
uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main
with:
build_type: ${{ inputs.build_type || 'branch' }}
branch: ${{ inputs.branch }}
date: ${{ inputs.date }}
sha: ${{ inputs.sha }}
node_type: "cpu4"
arch: "amd64"
container_image: "rapidsai/ci-conda:26.10-latest"
script: "ci/build_java.sh"
artifact-name: "cuopt-java"
file_to_upload: "java/cuopt/target/"
python-build:
needs: [build-details, cpp-build]
permissions:
Expand Down Expand Up @@ -288,6 +309,7 @@ jobs:
# so 'test.yaml' can be triggered without waiting for those.
needs:
- upload-conda
- java-build
- wheel-publish-cuopt
- wheel-publish-cuopt-server
- wheel-publish-cuopt-sh-client
Expand Down
45 changes: 44 additions & 1 deletion .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
- checks
- conda-cpp-build
- conda-cpp-tests
- java-build
- conda-python-build
- conda-python-tests
- docs-build
Expand Down Expand Up @@ -108,19 +109,22 @@ jobs:
- '!README.md'
- '!SECURITY.md'
- '!ci/build_wheel*.sh'
- '!ci/build_java.sh'
- '!ci/check_style.sh'
- '!ci/docker/**'
- '!ci/release/**'
- '!ci/run_*.pytests.sh'
- '!ci/run_ctests.sh'
- '!ci/test_cpp*.sh'
- '!ci/test_java.sh'
- '!ci/test_notebooks.sh'
- '!ci/test_python.sh'
- '!ci/test_self_hosted_service.sh'
- '!ci/test_wheel*.sh'
- '!ci/thirdparty-testing/**'
- '!container-builder/**'
- '!helmchart/**'
- '!java/**'
- '!ngc/**'
- '!sonar-project.properties'
- '!sonarqube/**'
Expand Down Expand Up @@ -168,12 +172,14 @@ jobs:
- '!SECURITY.md'
- '!agents/**'
- '!ci/build_docs.sh'
- '!ci/build_java.sh'
- '!ci/build_python.sh'
- '!ci/build_wheel*.sh'
- '!ci/check_style.sh'
- '!ci/docker/**'
- '!ci/release/**'
- '!ci/test_python.sh'
- '!ci/test_java.sh'
- '!ci/test_self_hosted_service.sh'
- '!ci/test_wheel*.sh'
- '!ci/thirdparty-testing/**'
Expand All @@ -184,6 +190,7 @@ jobs:
- '!gemini-extension.json'
- '!helmchart/**'
- '!img/**'
- '!java/**'
- '!ngc/**'
- '!notebooks/**'
- '!python/**'
Expand All @@ -197,6 +204,14 @@ jobs:
- '!sonarqube/**'
- '!ucf/**'
- '!utilities/**'
test_java:
- 'java/**'
- 'ci/build_java.sh'
- 'ci/test_java.sh'
- 'dependencies.yaml'
- '.github/workflows/pr.yaml'
- '.github/workflows/build.yaml'
- '.github/workflows/test.yaml'
test_python_conda:
- '**'
- '!**/*.md'
Expand Down Expand Up @@ -239,11 +254,13 @@ jobs:
- '!SECURITY.md'
- '!agents/**'
- '!ci/build_docs.sh'
- '!ci/build_java.sh'
- '!ci/build_wheel*.sh'
- '!ci/check_style.sh'
- '!ci/docker/**'
- '!ci/release/**'
- '!ci/test_self_hosted_service.sh'
- '!ci/test_java.sh'
- '!ci/test_wheel*.sh'
- '!ci/thirdparty-testing/**'
- '!ci/utils/sync_skills_version.sh'
Expand All @@ -253,6 +270,7 @@ jobs:
- '!gemini-extension.json'
- '!helmchart/**'
- '!img/**'
- '!java/**'
- '!ngc/**'
- '!notebooks/**'
- '!skills/**/SKILL.md'
Expand Down Expand Up @@ -309,11 +327,13 @@ jobs:
- '!ci/build_cpp.sh'
- '!ci/build_docs.sh'
- '!ci/build_python.sh'
- '!ci/build_java.sh'
- '!ci/check_style.sh'
- '!ci/docker/**'
- '!ci/release/**'
- '!ci/run_ctests.sh'
- '!ci/test_python.sh'
- '!ci/test_java.sh'
- '!ci/thirdparty-testing/**'
- '!ci/utils/sync_skills_version.sh'
- '!ci/utils/validate_skills.sh'
Expand All @@ -322,6 +342,7 @@ jobs:
- '!gemini-extension.json'
- '!helmchart/**'
- '!img/**'
- '!java/**'
- '!ngc/**'
- '!notebooks/**'
- '!skills/**/SKILL.md'
Expand All @@ -343,9 +364,10 @@ jobs:
ignored_pr_jobs: "pr-test-summary"
conda-cpp-build:
needs: [build-details, checks, compute-matrix-filters, changed-files]
# Consumed by conda-cpp-tests, conda-python-build, and (transitively) docs-build.
# Consumed by C++, Java, Python, and docs jobs.
if: >-
fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp ||
fromJSON(needs.changed-files.outputs.changed_file_groups).test_java ||
fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_conda ||
fromJSON(needs.changed-files.outputs.changed_file_groups).build_docs
permissions:
Expand Down Expand Up @@ -443,6 +465,27 @@ jobs:
artifact-name: "cuopt_docs"
container_image: "rapidsai/ci-conda:26.10-latest"
script: "ci/build_docs.sh"
java-build:
needs: [conda-cpp-build, changed-files]
permissions:
actions: read
contents: read
id-token: write
packages: read
pull-requests: read
secrets: inherit # zizmor: ignore[secrets-inherit]
uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main
if: >-
fromJSON(needs.changed-files.outputs.changed_file_groups).test_java ||
fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp
with:
build_type: pull-request
node_type: "gpu-l4-latest-1"
arch: "amd64"
container_image: "rapidsai/ci-conda:26.10-latest"
script: "ci/test_java.sh"
artifact-name: "cuopt-java"
file_to_upload: "java/cuopt/target/"
wheel-build-libcuopt:
needs: [build-details, compute-matrix-filters, changed-files]
if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_wheels
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,25 @@ jobs:
script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY
script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }}

conda-java-tests:
permissions:
actions: read
contents: read
id-token: write
packages: read
pull-requests: read
secrets: inherit # zizmor: ignore[secrets-inherit]
uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main
with:
build_type: ${{ inputs.build_type }}
branch: ${{ inputs.branch }}
date: ${{ inputs.date }}
sha: ${{ inputs.sha }}
node_type: "gpu-l4-latest-1"
arch: "amd64"
container_image: "rapidsai/ci-conda:26.10-latest"
script: "ci/test_java.sh"

wheel-tests-cuopt:
permissions:
actions: read
Expand Down Expand Up @@ -157,6 +176,7 @@ jobs:
needs:
- conda-cpp-tests
- conda-python-tests
- conda-java-tests
- wheel-tests-cuopt
- wheel-tests-cuopt-server
- conda-notebook-tests
Expand Down
50 changes: 49 additions & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ ARGS=$*
REPODIR=$(cd "$(dirname "$0")"; pwd)
LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build}

VALIDARGS="clean codegen libcuopt cuopt_grpc_server cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"<args>\\\"] [--cache-tool=<tool>] --install --allgpuarch --ci-only-arch --show_depr_warn -h --help"
VALIDARGS="clean codegen libcuopt cuopt_grpc_server cuopt cuopt_server cuopt_sh_client java docs deb --run-java-tests -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"<args>\\\"] [--cache-tool=<tool>] --install --allgpuarch --ci-only-arch --show_depr_warn -h --help"
HELP="$0 [<target> ...] [<flag> ...]
where <target> is:
clean - remove all existing build artifacts and configuration (start over)
Expand All @@ -24,6 +24,7 @@ HELP="$0 [<target> ...] [<flag> ...]
cuopt - build the cuopt Python package
cuopt_server - build the cuopt_server Python package
cuopt_sh_client - build cuopt self host client
java - build the cuopt Java bindings (requires libcuopt; needs maven and a JDK)
docs - build the docs
deb - build deb package (requires libcuopt to be built first)
and <flag> is:
Expand All @@ -36,6 +37,7 @@ HELP="$0 [<target> ...] [<flag> ...]
-msan - Build with MemorySanitizer (cannot be used with -fsanitize or -tsan)
--install - install built libraries into the active conda environment (default: build only, no install)
--no-fetch-rapids - don't fetch rapids dependencies
--run-java-tests - run the Java test suite as part of the 'java' target (needs a GPU)
-l= - log level. Options are: TRACE | DEBUG | INFO | WARN | ERROR | CRITICAL | OFF. Default=INFO
--verbose-pdlp - verbose mode for pdlp solver
--build-lp-only - build only linear programming components, excluding routing package and MIP-specific files
Expand Down Expand Up @@ -448,6 +450,52 @@ if buildAll || hasArg cuopt_sh_client; then
python "${PYTHON_ARGS_FOR_INSTALL[@]}" .
fi

# Build the Java bindings (opt-in; pass 'java' explicitly to build)
if hasArg java; then
if [ ! -f "${LIBCUOPT_BUILD_DIR}/libcuopt.so" ] && [ ! -f "${INSTALL_PREFIX}/lib/libcuopt.so" ]; then
echo "libcuopt was not found. Build it first: ./build.sh libcuopt"
exit 1
fi

# Prefer the build tree, so 'java' works without --install. libcuopt is compiled against
# the CPM-fetched rmm and raft under _deps, and rmm encodes its version in an inline
# namespace, so the JNI layer has to see those exact headers rather than any copy in the
# conda prefix. Mixing them links cleanly and then fails at dlopen with an undefined symbol.
if [ -f "${LIBCUOPT_BUILD_DIR}/libcuopt.so" ]; then
export CUOPT_LIBRARY="${LIBCUOPT_BUILD_DIR}/libcuopt.so"
JAVA_DEPS="${LIBCUOPT_BUILD_DIR}/_deps"
export CUOPT_EXTRA_INCLUDE_DIRS="${REPODIR}/cpp/include;${LIBCUOPT_BUILD_DIR}/include"
for dep in \
"${JAVA_DEPS}/rmm-src/cpp/include" \
"${JAVA_DEPS}/rmm-build/include" \
"${JAVA_DEPS}/raft-src/cpp/include" \
"${JAVA_DEPS}/raft-build/include" \
"${JAVA_DEPS}/rapids_logger-src/include" \
"${JAVA_DEPS}/cccl-src/thrust" \
"${JAVA_DEPS}/cccl-src/libcudacxx/include" \
"${JAVA_DEPS}/cccl-src/cub"; do
if [ -d "${dep}" ]; then
CUOPT_EXTRA_INCLUDE_DIRS="${CUOPT_EXTRA_INCLUDE_DIRS};${dep}"
fi
done
# Same reasoning at run time. libcuopt's RPATH lists the conda prefix first and DT_RPATH
# is searched ahead of LD_LIBRARY_PATH, so the CPM copies also have to be preloaded.
export CUOPT_EXTRA_LIBRARY_DIRS="${JAVA_DEPS}/rmm-build:${JAVA_DEPS}/rapids_logger-build"
export CUOPT_PRELOAD_LIBS="${JAVA_DEPS}/rmm-build/librmm.so ${JAVA_DEPS}/rapids_logger-build/librapids_logger.so"
export CUOPT_RUNTIME_LIBRARY_DIR="${LIBCUOPT_BUILD_DIR}"
fi

export CUOPT_JAVA_NATIVE_BUILD_DIR="${CUOPT_JAVA_NATIVE_BUILD_DIR:-${REPODIR}/java/cuopt/build/native}"
if hasArg --run-java-tests; then
bash "${REPODIR}"/java/cuopt/scripts/test.sh
else
bash "${REPODIR}"/java/cuopt/scripts/build_native.sh
mvn -f "${REPODIR}"/java/cuopt/pom.xml clean package \
-DskipTests \
-Dcuopt.native.dir="${CUOPT_JAVA_NATIVE_BUILD_DIR}"
fi
fi

# Build the docs (opt-in; pass 'docs' explicitly to build)
if hasArg docs; then
cd "${REPODIR}"/cpp/doxygen
Expand Down
55 changes: 55 additions & 0 deletions ci/build_java.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/bin/bash

# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail

RUN_TESTS=false
if [[ "${1:-}" == "--run-java-tests" ]]; then
RUN_TESTS=true
elif [[ $# -gt 0 ]]; then
echo "Usage: $0 [--run-java-tests]" >&2
exit 2
fi

if [[ -e /opt/conda/etc/profile.d/conda.sh ]]; then
. /opt/conda/etc/profile.d/conda.sh
fi

rapids-logger "Configuring conda strict channel priority"
conda config --set channel_priority strict

rapids-logger "Downloading the C++ artifact"
CPP_CHANNEL=$(rapids-download-from-github \
"$(rapids-artifact-name conda_cpp libcuopt cuopt --cuda "$RAPIDS_CUDA_VERSION")")

rapids-logger "Generating Java build dependencies"
ENV_YAML_DIR=$(mktemp -d)
rapids-dependency-file-generator \
--output conda \
--file-key java \
--prepend-channel "${CPP_CHANNEL}" \
--matrix "cuda=${RAPIDS_CUDA_VERSION%.*};arch=$(arch)" | tee "${ENV_YAML_DIR}/env.yaml"

rapids-mamba-retry env create --yes -f "${ENV_YAML_DIR}/env.yaml" -n java \
--channel "${CPP_CHANNEL}"

# Temporarily allow unbound variables for conda activation.
set +u
conda activate java
set -u

rapids-print-env

export CUOPT_PREFIX="${CONDA_PREFIX}"

# libcuopt comes from the conda artifact here, not a local build tree, so build.sh's 'java'
# target uses the install prefix directly.
if [[ "${RUN_TESTS}" == true ]]; then
rapids-logger "Building and testing the Java bindings"
./build.sh java --run-java-tests
else
rapids-logger "Building the Java bindings"
./build.sh java
fi
8 changes: 8 additions & 0 deletions ci/release/update-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ for DEP in "${DEPENDENCIES[@]}"; do
done
done

# Update the Java API version. Maven has no notion of the zero-padded RAPIDS patch field, and a
# padded patch would sort oddly against a later unpadded one, so the padding is stripped here.
# This matches how cuvs versions its Java artifact.
NEXT_FULL_JAVA_TAG=$(echo "$NEXT_FULL_TAG" | sed -E 's/^([0-9]+)\.([0-9]+)\.0*([0-9]+)$/\1.\2.\3/')
for FILE in java/*/pom.xml; do
sed_runner "/<!--CUOPT_JAVA#VERSION_UPDATE_MARKER_START-->.*<!--CUOPT_JAVA#VERSION_UPDATE_MARKER_END-->/s//<!--CUOPT_JAVA#VERSION_UPDATE_MARKER_START--><version>${NEXT_FULL_JAVA_TAG}<\/version><!--CUOPT_JAVA#VERSION_UPDATE_MARKER_END-->/g" "${FILE}"
done

# Update README.md version badge
sed_runner 's/badge\/version-[0-9]\+\.[0-9]\+\.[0-9]\+-blue/badge\/version-'${NEXT_FULL_TAG}'-blue/g' README.md

Expand Down
12 changes: 12 additions & 0 deletions ci/test_java.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail

rapids-logger "Checking GPU availability"
nvidia-smi

rapids-logger "Running the Java build and tests"
ci/build_java.sh --run-java-tests
Loading