From 636c9de2125be56b96a10c2a4b8bb61153c85f93 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 28 Sep 2026 13:06:34 -0500 Subject: [PATCH 01/14] Link Java bindings from top-level docs entry points Java has a full docs section (quick-start, convex/MIP API reference, examples) already wired into the toctree, but introduction.rst, install.rst, convex-features.rst, and milp-features.rst never mentioned it alongside Python/C, so it wasn't discoverable from the pages users actually land on first. Co-Authored-By: Claude Sonnet 5 --- docs/cuopt/source/convex-features.rst | 2 ++ docs/cuopt/source/install.rst | 1 + docs/cuopt/source/introduction.rst | 2 ++ docs/cuopt/source/milp-features.rst | 2 ++ 4 files changed, 7 insertions(+) diff --git a/docs/cuopt/source/convex-features.rst b/docs/cuopt/source/convex-features.rst index 82de2dbc78..5120b1ca9e 100644 --- a/docs/cuopt/source/convex-features.rst +++ b/docs/cuopt/source/convex-features.rst @@ -51,6 +51,8 @@ The convex optimization solvers for Linear Programming (LP), Quadratic Programmi - **Python SDK**: A Python package that provides direct access to cuOpt's convex optimization solvers through a simple, intuitive API. This allows for seamless integration into Python applications and workflows. For more information, see :doc:`cuopt-python/quick-start`. +- **Java (experimental)**: JNI bindings that provide direct access to cuOpt's convex optimization solvers from Java applications. For more information, see :doc:`cuopt-java/quick-start`. + - **As a Self-Hosted Service**: cuOpt's convex optimization solvers can be deployed as a self-hosted service in your own infrastructure, enabling you to maintain full control while integrating it into your existing systems. Each option provides access to the same powerful convex optimization solvers while offering flexibility in deployment and integration. diff --git a/docs/cuopt/source/install.rst b/docs/cuopt/source/install.rst index 404d7361f8..0d5a922141 100644 --- a/docs/cuopt/source/install.rst +++ b/docs/cuopt/source/install.rst @@ -16,6 +16,7 @@ If the selector does not load or you prefer step-by-step guides, use the quick-s * **Python (cuopt)** — :doc:`cuopt-python/quick-start` * **C (libcuopt)** — :doc:`cuopt-c/quick-start` (includes ``cuopt_cli``) +* **Java (cuopt, experimental)** — :doc:`cuopt-java/quick-start` (built from source against an existing cuOpt installation; not distributed via the install selector above) * **gRPC remote execution** — :doc:`cuopt-grpc/quick-start` (install, remote execution, Docker, minimal example) and :doc:`cuopt-grpc/advanced` (TLS and tuning; not the HTTP server) * **Server (cuopt-server)** — :doc:`cuopt-server/quick-start` * **CLI (cuopt_cli)** — Install via the C API; see :doc:`cuopt-cli/quick-start` diff --git a/docs/cuopt/source/introduction.rst b/docs/cuopt/source/introduction.rst index 3c61684316..7634c6d98a 100644 --- a/docs/cuopt/source/introduction.rst +++ b/docs/cuopt/source/introduction.rst @@ -126,6 +126,8 @@ cuOpt supports the following APIs: - Python support - :doc:`Routing (TSP, VRP, and PDP) - Python ` - :doc:`Linear Programming (LP) / Quadratic Programming (QP) and Mixed Integer Programming (MIP) - Python ` +- Java support (experimental) + - :doc:`Linear Programming (LP) / Quadratic Programming (QP) and Mixed Integer Programming (MIP) - Java ` - gRPC remote execution and gRPC clients - :doc:`Remote execution (zero code change) ` — set ``CUOPT_REMOTE_HOST`` / ``CUOPT_REMOTE_PORT``; Python, C (``cuOptSolve``), and ``cuopt_cli`` forward automatically - :doc:`Python async gRPC client ` — explicit job API (submit / wait / cancel / stream logs and incumbents) diff --git a/docs/cuopt/source/milp-features.rst b/docs/cuopt/source/milp-features.rst index 4d184316e7..48e05b28f0 100644 --- a/docs/cuopt/source/milp-features.rst +++ b/docs/cuopt/source/milp-features.rst @@ -25,6 +25,8 @@ The MIP solver can be accessed in the following ways: - **Python SDK**: A Python package that provides direct access to cuOpt's MIP capabilities through a simple, intuitive API. This allows for seamless integration into Python applications and workflows. For more information, see :doc:`cuopt-python/quick-start`. +- **Java (experimental)**: JNI bindings that provide direct access to cuOpt's MIP solver from Java applications. For more information, see :doc:`cuopt-java/quick-start`. + - **As a Self-Hosted Service**: cuOpt's MIP solver can be deployed in your own infrastructure, enabling you to maintain full control while integrating it into your existing systems. Each option provides the same mixed-integer optimization capabilities while offering flexibility in deployment and integration. From 5bd050779df525c1d62665ad7b30d847014d8034 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 28 Sep 2026 15:31:07 -0500 Subject: [PATCH 02/14] docs(java): document Docker image and Maven artifact consumption paths quick-start.rst only covered building the bindings from source. Add sections for the two ways most consumers will actually get them: the prebuilt cuopt.jar/libcuopt_jni.so already in the official Docker images, and the com.nvidia.cuopt:cuopt Maven classifier jars. Note that the classifier jars embed libcuopt and its RAPIDS dependencies but not the CUDA toolkit's own math libraries, so the target system still needs a CUDA runtime. --- docs/cuopt/source/cuopt-java/quick-start.rst | 54 ++++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index 7563540809..ed39871eb8 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -1,10 +1,17 @@ Java Quick Start ================ -The experimental Java bindings live in ``java/cuopt`` and are built explicitly -from source. Repository CI and release workflows also build and test the module -against the matching ``libcuopt`` artifact. It is not part of the top-level -cuOpt build, and a supported Maven distribution has not yet been defined. +The experimental Java bindings live in ``java/cuopt``. There are three ways to +get them, depending on your setup: + +* the official cuOpt Docker images already contain a prebuilt ``cuopt.jar`` + and ``libcuopt_jni.so`` — see `Using the Docker Image`_; +* the ``com.nvidia.cuopt:cuopt`` Maven artifact is a self-contained classifier + jar that embeds the native library — see `Using the Maven Artifact`_; or +* building from source, which this section covers first and which repository + CI and release workflows use to produce both of the above. + +It is not part of the top-level cuOpt build. Requirements ------------ @@ -78,6 +85,45 @@ native library's runtime path. The standalone native build embeds the CUDA runtime path for the configured ``CUOPT_PREFIX``; the helper script also exports it for Maven. +Using the Docker Image +---------------------- + +The official cuOpt Docker images ship ``cuopt.jar`` and ``libcuopt_jni.so`` +under ``/opt/cuopt/java``, built against the image's own ``libcuopt.so``. No +build step is needed; point ``cuopt.native.dir`` at that directory: + +.. code-block:: bash + + docker run --rm --gpus all -v $(pwd):/work -w /work bash -c ' + javac -cp /opt/cuopt/java/cuopt.jar -d . MyProgram.java + java -Dcuopt.native.dir=/opt/cuopt/java -cp /opt/cuopt/java/cuopt.jar:. MyProgram + ' + +Using the Maven Artifact +------------------------ + +``com.nvidia.cuopt:cuopt`` publishes classifier jars (``cuda12``, +``cuda12-arm64``, ``cuda13``, ``cuda13-arm64``) to the Sonatype snapshot and +release repositories. Each classifier jar embeds ``libcuopt_jni.so`` and +cuOpt's own native dependencies (``libcuopt``, rmm, cuDSS, NCCL, TBB), which +``NativeLibraryLoader`` extracts to a temp directory and loads automatically — +no ``cuopt.native.dir`` is required: + +.. code-block:: xml + + + com.nvidia.cuopt + cuopt + 26.10.0-SNAPSHOT + cuda12 + + +The embedded libraries do not include the CUDA toolkit's own math libraries +(``libcublas``, ``libcusolver``, etc.) — those must already be present on the +target system, e.g. via an ``nvidia/cuda:*-runtime-*`` base image or an +equivalent CUDA runtime install. Loading the jar on a system without them +fails with an ``UnsatisfiedLinkError`` naming the missing CUDA library. + LP Example ---------- From d8ba39b412f1eee8255eb5a759bccf28011b4a36 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 28 Sep 2026 15:46:42 -0500 Subject: [PATCH 03/14] docs(java): add a runnable example for the Maven artifact's CUDA runtime requirement The prior note said the CUDA runtime libraries "must already be present" without showing how. Add a docker run example against nvidia/cuda:*-runtime-* mirroring the container actually used to verify this end to end. --- docs/cuopt/source/cuopt-java/quick-start.rst | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index ed39871eb8..004bed6d42 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -120,9 +120,20 @@ no ``cuopt.native.dir`` is required: The embedded libraries do not include the CUDA toolkit's own math libraries (``libcublas``, ``libcusolver``, etc.) — those must already be present on the -target system, e.g. via an ``nvidia/cuda:*-runtime-*`` base image or an -equivalent CUDA runtime install. Loading the jar on a system without them -fails with an ``UnsatisfiedLinkError`` naming the missing CUDA library. +target system. Loading the jar on a system without them fails with an +``UnsatisfiedLinkError`` naming the missing CUDA library. An +``nvidia/cuda:*-runtime-*`` base image satisfies this without installing +cuOpt itself: + +.. code-block:: bash + + docker run --rm --gpus all -v $(pwd):/work -w /work \ + nvidia/cuda:12.9.0-runtime-ubuntu24.04 bash -c ' + apt-get update -qq && apt-get install -y -qq openjdk-17-jdk-headless maven + mvn -q dependency:copy-dependencies -DoutputDirectory=lib + javac -cp "lib/cuopt-*-cuda12.jar" -d . MyProgram.java + java -cp "lib/cuopt-*-cuda12.jar:." MyProgram + ' LP Example ---------- From 8a2e9b62de823dbf94654c0679e9602f557ec591 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 28 Sep 2026 15:53:55 -0500 Subject: [PATCH 04/14] docs(java): clarify example source and add a non-Docker install path Point MyProgram.java at the LP Example below instead of leaving it undefined, and note the apt/dnf cuda-libraries package for consumers not using a Docker base image. --- docs/cuopt/source/cuopt-java/quick-start.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index 004bed6d42..abb95a9095 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -90,7 +90,10 @@ Using the Docker Image The official cuOpt Docker images ship ``cuopt.jar`` and ``libcuopt_jni.so`` under ``/opt/cuopt/java``, built against the image's own ``libcuopt.so``. No -build step is needed; point ``cuopt.native.dir`` at that directory: +build step is needed; point ``cuopt.native.dir`` at that directory. Mount a +directory containing your own ``.java`` source (for example, the LP Example +below saved as ``MyProgram.java``) and compile and run it against the +prebuilt jar: .. code-block:: bash @@ -123,7 +126,9 @@ The embedded libraries do not include the CUDA toolkit's own math libraries target system. Loading the jar on a system without them fails with an ``UnsatisfiedLinkError`` naming the missing CUDA library. An ``nvidia/cuda:*-runtime-*`` base image satisfies this without installing -cuOpt itself: +cuOpt itself. The example below assumes a project with the ```` +above in its ``pom.xml`` and a ``MyProgram.java`` source (for example, the +LP Example below) in the working directory: .. code-block:: bash @@ -135,6 +140,11 @@ cuOpt itself: java -cp "lib/cuopt-*-cuda12.jar:." MyProgram ' +Outside Docker, install the matching ``cuda-libraries--`` +package (e.g. ``cuda-libraries-12-9``) from `NVIDIA's CUDA repository +`_ via ``apt-get`` or ``dnf`` +instead of the full CUDA toolkit. + LP Example ---------- From f080e6ece0add2b0d1a17d51db8247e5d006caed Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 28 Sep 2026 15:56:15 -0500 Subject: [PATCH 05/14] docs(java): address review feedback Capitalize bullet points, rename to "Java Quickstart Guide" to match the "Quickstart" spelling used elsewhere in the docs, and update install.rst's Java entry now that it's distributed via Docker and Maven, not source-build only. --- docs/cuopt/source/cuopt-java/quick-start.rst | 16 ++++++++-------- docs/cuopt/source/install.rst | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index abb95a9095..a52e85adda 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -1,14 +1,14 @@ -Java Quick Start -================ +Java Quickstart Guide +===================== The experimental Java bindings live in ``java/cuopt``. There are three ways to get them, depending on your setup: -* the official cuOpt Docker images already contain a prebuilt ``cuopt.jar`` +* The official cuOpt Docker images already contain a prebuilt ``cuopt.jar`` and ``libcuopt_jni.so`` — see `Using the Docker Image`_; -* the ``com.nvidia.cuopt:cuopt`` Maven artifact is a self-contained classifier +* The ``com.nvidia.cuopt:cuopt`` Maven artifact is a self-contained classifier jar that embeds the native library — see `Using the Maven Artifact`_; or -* building from source, which this section covers first and which repository +* Building from source, which this section covers first and which repository CI and release workflows use to produce both of the above. It is not part of the top-level cuOpt build. @@ -19,9 +19,9 @@ Requirements The Java module requires: * Java 17 or newer, with ``JAVA_HOME`` pointing to a JDK; -* a C++20 compiler; -* an existing cuOpt installation containing ``libcuopt.so``; and -* a CUDA-enabled runtime for solving problems. +* A C++20 compiler; +* An existing cuOpt installation containing ``libcuopt.so``; and +* A CUDA-enabled runtime for solving problems. The module uses Maven for Java compilation and a Java-local CMake project for the JNI library. The standalone native build links to diff --git a/docs/cuopt/source/install.rst b/docs/cuopt/source/install.rst index 0d5a922141..76cf888fbb 100644 --- a/docs/cuopt/source/install.rst +++ b/docs/cuopt/source/install.rst @@ -16,7 +16,7 @@ If the selector does not load or you prefer step-by-step guides, use the quick-s * **Python (cuopt)** — :doc:`cuopt-python/quick-start` * **C (libcuopt)** — :doc:`cuopt-c/quick-start` (includes ``cuopt_cli``) -* **Java (cuopt, experimental)** — :doc:`cuopt-java/quick-start` (built from source against an existing cuOpt installation; not distributed via the install selector above) +* **Java (cuopt, experimental)** — :doc:`cuopt-java/quick-start` (official Docker images, the ``com.nvidia.cuopt:cuopt`` Maven artifact, or built from source; not distributed via the install selector above) * **gRPC remote execution** — :doc:`cuopt-grpc/quick-start` (install, remote execution, Docker, minimal example) and :doc:`cuopt-grpc/advanced` (TLS and tuning; not the HTTP server) * **Server (cuopt-server)** — :doc:`cuopt-server/quick-start` * **CLI (cuopt_cli)** — Install via the C API; see :doc:`cuopt-cli/quick-start` From 0e64c3132011ab12ce60aa1ef5e8e6cd2903f4c6 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 08:18:48 -0500 Subject: [PATCH 06/14] docs(java): trim the Docker image section to match other interfaces Python/C/CLI quick-starts don't showcase Docker at all; only gRPC does, and it links to install.rst rather than duplicating a run script. Do the same here instead of inventing a Java-specific pattern. --- docs/cuopt/source/cuopt-java/quick-start.rst | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index a52e85adda..b339188393 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -90,17 +90,9 @@ Using the Docker Image The official cuOpt Docker images ship ``cuopt.jar`` and ``libcuopt_jni.so`` under ``/opt/cuopt/java``, built against the image's own ``libcuopt.so``. No -build step is needed; point ``cuopt.native.dir`` at that directory. Mount a -directory containing your own ``.java`` source (for example, the LP Example -below saved as ``MyProgram.java``) and compile and run it against the -prebuilt jar: - -.. code-block:: bash - - docker run --rm --gpus all -v $(pwd):/work -w /work bash -c ' - javac -cp /opt/cuopt/java/cuopt.jar -d . MyProgram.java - java -Dcuopt.native.dir=/opt/cuopt/java -cp /opt/cuopt/java/cuopt.jar:. MyProgram - ' +build step is needed; compile and run your code with +``-cp /opt/cuopt/java/cuopt.jar`` and ``-Dcuopt.native.dir=/opt/cuopt/java``. +See :doc:`../install` for image tags. Using the Maven Artifact ------------------------ From 263e09d35fb1988b6b5b86387b47d2bd99eede67 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 09:07:23 -0500 Subject: [PATCH 07/14] docs(java): fix CodeRabbit findings - Add Sonatype snapshot repo to the Maven dependency example - Scope "Requirements" to the source build, not the whole module - Use lib/* classpath wildcard (basename-only, not partial filename) - Clarify -cp applies to javac+java, -Dcuopt.native.dir to java only --- docs/cuopt/source/cuopt-java/quick-start.rst | 21 ++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index b339188393..02e0fbfa7d 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -16,7 +16,7 @@ It is not part of the top-level cuOpt build. Requirements ------------ -The Java module requires: +The source build requires: * Java 17 or newer, with ``JAVA_HOME`` pointing to a JDK; * A C++20 compiler; @@ -90,9 +90,9 @@ Using the Docker Image The official cuOpt Docker images ship ``cuopt.jar`` and ``libcuopt_jni.so`` under ``/opt/cuopt/java``, built against the image's own ``libcuopt.so``. No -build step is needed; compile and run your code with -``-cp /opt/cuopt/java/cuopt.jar`` and ``-Dcuopt.native.dir=/opt/cuopt/java``. -See :doc:`../install` for image tags. +build step is needed. Use ``-cp /opt/cuopt/java/cuopt.jar`` for both +compilation and execution; pass ``-Dcuopt.native.dir=/opt/cuopt/java`` only to +the ``java`` command. See :doc:`../install` for image tags. Using the Maven Artifact ------------------------ @@ -106,6 +106,15 @@ no ``cuopt.native.dir`` is required: .. code-block:: xml + + + sonatype-snapshots + https://central.sonatype.com/repository/maven-snapshots + false + true + + + com.nvidia.cuopt cuopt @@ -128,8 +137,8 @@ LP Example below) in the working directory: nvidia/cuda:12.9.0-runtime-ubuntu24.04 bash -c ' apt-get update -qq && apt-get install -y -qq openjdk-17-jdk-headless maven mvn -q dependency:copy-dependencies -DoutputDirectory=lib - javac -cp "lib/cuopt-*-cuda12.jar" -d . MyProgram.java - java -cp "lib/cuopt-*-cuda12.jar:." MyProgram + javac -cp "lib/*" -d . MyProgram.java + java -cp "lib/*:." MyProgram ' Outside Docker, install the matching ``cuda-libraries--`` From 7ef2c5c35747c0821c5e51c535a5444d965f439b Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 09:15:23 -0500 Subject: [PATCH 08/14] docs(java): wire Java into the install selector, match sibling quickstarts - install-selector.js/conf.py: add java iface (container method only, reuses the existing image data; no pip/conda package exists) - quick-start.rst: restructure to match Python/C/Server/CLI shape -- Installation (selector) + Smoke Test, drop the source-build section (covered in java/cuopt/README.md) and the standalone Docker section (now covered by the selector); keep Maven since it's Java-specific and not in the selector (no stable release yet, no arch axis) --- docs/cuopt/source/_static/install-selector.js | 8 +- docs/cuopt/source/conf.py | 4 +- docs/cuopt/source/cuopt-java/quick-start.rst | 160 ++++++------------ 3 files changed, 64 insertions(+), 108 deletions(-) diff --git a/docs/cuopt/source/_static/install-selector.js b/docs/cuopt/source/_static/install-selector.js index ceeff29e65..c44dc93538 100644 --- a/docs/cuopt/source/_static/install-selector.js +++ b/docs/cuopt/source/_static/install-selector.js @@ -243,6 +243,10 @@ }, }, }, + /* Java has no pip/conda package; the Docker images already contain cuopt.jar. */ + java: { + container: CONTAINER_CUOPT_LIB, + }, }; var SUPPORTED_METHODS = { @@ -250,6 +254,7 @@ c: ["pip", "conda", "container"], server: ["pip", "conda", "container"], cli: ["pip", "conda", "container"], + java: ["container"], }; function getSelectedValue(name) { @@ -404,6 +409,7 @@ '' + '' + '' + + '' + '' + 'Method' + '' + @@ -444,7 +450,7 @@ updateVisibility(); var defaultIface = root.getAttribute("data-default-iface"); - if (defaultIface && ["python", "c", "server", "cli"].indexOf(defaultIface) !== -1) { + if (defaultIface && ["python", "c", "server", "cli", "java"].indexOf(defaultIface) !== -1) { var radio = document.querySelector('input[name="cuopt-iface"][value="' + defaultIface + '"]'); if (radio) { radio.checked = true; diff --git a/docs/cuopt/source/conf.py b/docs/cuopt/source/conf.py index f817b22a2e..515576e5a6 100644 --- a/docs/cuopt/source/conf.py +++ b/docs/cuopt/source/conf.py @@ -432,7 +432,7 @@ def write_project_json(app, _builder): class InstallSelector(Directive): - """Embed the install selector widget. Optional :default-iface: (python, c, server, cli).""" + """Embed the install selector widget. Optional :default-iface: (python, c, server, cli, java).""" optional_arguments = 0 option_spec = {"default-iface": directives.unchanged} @@ -442,7 +442,7 @@ def run(self): default_iface = ( (self.options.get("default-iface") or "").strip().lower() ) - if default_iface not in ("python", "c", "server", "cli"): + if default_iface not in ("python", "c", "server", "cli", "java"): default_iface = "" data_attr = ( ' data-default-iface="' + default_iface + '"' diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index 02e0fbfa7d..236b5cf5b3 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -1,101 +1,25 @@ Java Quickstart Guide ===================== -The experimental Java bindings live in ``java/cuopt``. There are three ways to -get them, depending on your setup: +NVIDIA cuOpt provides experimental Java bindings for LP, MIP, QP, QCQP, and +SOCP, built from ``java/cuopt``. It is not part of the top-level cuOpt build. -* The official cuOpt Docker images already contain a prebuilt ``cuopt.jar`` - and ``libcuopt_jni.so`` — see `Using the Docker Image`_; -* The ``com.nvidia.cuopt:cuopt`` Maven artifact is a self-contained classifier - jar that embeds the native library — see `Using the Maven Artifact`_; or -* Building from source, which this section covers first and which repository - CI and release workflows use to produce both of the above. +Installation +============ -It is not part of the top-level cuOpt build. +Choose your install method below; the selector is pre-set for Java. Copy the +Docker command and run it in your environment — ``cuopt.jar`` and +``libcuopt_jni.so`` are already at ``/opt/cuopt/java`` inside the container, +so no build step is needed. Use ``-cp /opt/cuopt/java/cuopt.jar`` for both +compilation and execution, and pass ``-Dcuopt.native.dir=/opt/cuopt/java`` +only to the ``java`` command. See :doc:`../install` for all interfaces and +options. -Requirements ------------- - -The source build requires: - -* Java 17 or newer, with ``JAVA_HOME`` pointing to a JDK; -* A C++20 compiler; -* An existing cuOpt installation containing ``libcuopt.so``; and -* A CUDA-enabled runtime for solving problems. - -The module uses Maven for Java compilation and a Java-local CMake project for -the JNI library. The standalone native build links to -``$CUOPT_PREFIX/lib/libcuopt.so`` and places ``libcuopt_jni.so`` under -``java/cuopt/build/native``. - -.. code-block:: bash - - cd /path/to/cuopt/java/cuopt - export JAVA_HOME=/path/to/jdk-17 - export CUOPT_PREFIX=/path/to/cuopt/conda/environment - bash scripts/build_native.sh - -This builds ``java/cuopt/build/native/libcuopt_jni.so``. Java is intentionally -not part of the default cuOpt build. - -To build the native library in a different directory, set -``CUOPT_JAVA_NATIVE_BUILD_DIR``. If CUDA headers are installed outside the -usual locations, pass ``-DCUOPT_CUDA_INCLUDE_DIR=/path/to/cuda/include`` to -the CMake configure step. - -Native Loading --------------- - -At runtime the bindings load ``libcuopt_jni``. For local development, point Java -at the directory containing the built native library: - -.. code-block:: bash - - cd java/cuopt - export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 - export CUOPT_PREFIX=/path/to/cuopt/conda/environment - export LD_LIBRARY_PATH=$CUOPT_PREFIX/targets/x86_64-linux/lib:$CUOPT_PREFIX/lib:build/native - mvn test -Dcuopt.native.dir=build/native - -The helper script combines the native build and Maven test steps: - -.. code-block:: bash - - cd /path/to/cuopt/java/cuopt - export JAVA_HOME=/path/to/jdk-17 - export CUOPT_PREFIX=/path/to/cuopt/conda/environment - bash scripts/test.sh - -To run one test class, pass its Maven property to the helper: - -.. code-block:: bash - - bash scripts/test.sh -Dtest=ProblemIntegrationTest - -Application code can use the same property: - -.. code-block:: bash - - java -Dcuopt.native.dir=/path/to/java/cuopt/build/native ... - -The Java classes load ``libcuopt_jni`` when the first binding object is -created. ``cuopt.native.dir`` must contain that library, and the cuOpt and -CUDA runtime libraries must be discoverable through ``LD_LIBRARY_PATH`` or the -native library's runtime path. The standalone native build embeds the CUDA -runtime path for the configured ``CUOPT_PREFIX``; the helper script also -exports it for Maven. - -Using the Docker Image ----------------------- - -The official cuOpt Docker images ship ``cuopt.jar`` and ``libcuopt_jni.so`` -under ``/opt/cuopt/java``, built against the image's own ``libcuopt.so``. No -build step is needed. Use ``-cp /opt/cuopt/java/cuopt.jar`` for both -compilation and execution; pass ``-Dcuopt.native.dir=/opt/cuopt/java`` only to -the ``java`` command. See :doc:`../install` for image tags. +.. install-selector:: + :default-iface: java Using the Maven Artifact ------------------------- +------------------------- ``com.nvidia.cuopt:cuopt`` publishes classifier jars (``cuda12``, ``cuda12-arm64``, ``cuda13``, ``cuda13-arm64``) to the Sonatype snapshot and @@ -127,24 +51,50 @@ The embedded libraries do not include the CUDA toolkit's own math libraries target system. Loading the jar on a system without them fails with an ``UnsatisfiedLinkError`` naming the missing CUDA library. An ``nvidia/cuda:*-runtime-*`` base image satisfies this without installing -cuOpt itself. The example below assumes a project with the ```` -above in its ``pom.xml`` and a ``MyProgram.java`` source (for example, the -LP Example below) in the working directory: +cuOpt itself; outside Docker, install the matching +``cuda-libraries--`` package (e.g. ``cuda-libraries-12-9``) from +`NVIDIA's CUDA repository `_ via +``apt-get`` or ``dnf`` instead of the full CUDA toolkit. + +Building from source is covered in ``java/cuopt/README.md``. + +Smoke Test +---------- + +After installation, verify cuOpt Java is working by compiling and running a +minimal LP inside the container: .. code-block:: bash - docker run --rm --gpus all -v $(pwd):/work -w /work \ - nvidia/cuda:12.9.0-runtime-ubuntu24.04 bash -c ' - apt-get update -qq && apt-get install -y -qq openjdk-17-jdk-headless maven - mvn -q dependency:copy-dependencies -DoutputDirectory=lib - javac -cp "lib/*" -d . MyProgram.java - java -cp "lib/*:." MyProgram - ' - -Outside Docker, install the matching ``cuda-libraries--`` -package (e.g. ``cuda-libraries-12-9``) from `NVIDIA's CUDA repository -`_ via ``apt-get`` or ``dnf`` -instead of the full CUDA toolkit. + cat > SmokeTest.java <<'EOF' + import com.nvidia.cuopt.mathematicaloptimization.*; + + public class SmokeTest { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("smoke-test")) { + Variable x = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, + VariableType.CONTINUOUS, "x"); + Variable y = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, + VariableType.CONTINUOUS, "y"); + problem.addConstraint(LinearExpression.of(x).plus(y).ge(1.0), "c0"); + problem.setObjective(LinearExpression.of(x).plus(y), ObjectiveSense.MINIMIZE); + try (Solution solution = problem.solve()) { + System.out.println(solution.getTerminationStatus()); + System.out.println(solution.getPrimalObjective()); + } + } + } + } + EOF + javac -cp /opt/cuopt/java/cuopt.jar -d . SmokeTest.java + java -Dcuopt.native.dir=/opt/cuopt/java -cp /opt/cuopt/java/cuopt.jar:. SmokeTest + +Example Response: + +.. code-block:: text + + OPTIMAL + 1.0 LP Example ---------- From d6e7dc7759578bf4ef4b680257c218934a8dfba4 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 09:15:42 -0500 Subject: [PATCH 09/14] docs(install): Java is now in the selector (container method) --- docs/cuopt/source/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cuopt/source/install.rst b/docs/cuopt/source/install.rst index 76cf888fbb..c037ca6ce6 100644 --- a/docs/cuopt/source/install.rst +++ b/docs/cuopt/source/install.rst @@ -16,7 +16,7 @@ If the selector does not load or you prefer step-by-step guides, use the quick-s * **Python (cuopt)** — :doc:`cuopt-python/quick-start` * **C (libcuopt)** — :doc:`cuopt-c/quick-start` (includes ``cuopt_cli``) -* **Java (cuopt, experimental)** — :doc:`cuopt-java/quick-start` (official Docker images, the ``com.nvidia.cuopt:cuopt`` Maven artifact, or built from source; not distributed via the install selector above) +* **Java (cuopt, experimental)** — :doc:`cuopt-java/quick-start` (Docker via the selector above, the ``com.nvidia.cuopt:cuopt`` Maven artifact, or built from source) * **gRPC remote execution** — :doc:`cuopt-grpc/quick-start` (install, remote execution, Docker, minimal example) and :doc:`cuopt-grpc/advanced` (TLS and tuning; not the HTTP server) * **Server (cuopt-server)** — :doc:`cuopt-server/quick-start` * **CLI (cuopt_cli)** — Install via the C API; see :doc:`cuopt-cli/quick-start` From 5b4a760acfd5aa226b92bcd41b30309e23014223 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 09:19:34 -0500 Subject: [PATCH 10/14] docs(java): add Maven method to the install selector - New Arch row (amd64/arm64), shown only for java+maven, same conditional pattern as the existing variant/registry rows - Stable version resolves to the release version; nightly to .0-SNAPSHOT with the Sonatype snapshot repo block - This will publish alongside the actual release, so a real stable Maven Central version will exist by then --- docs/cuopt/source/_static/install-selector.js | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/docs/cuopt/source/_static/install-selector.js b/docs/cuopt/source/_static/install-selector.js index c44dc93538..7463794a2f 100644 --- a/docs/cuopt/source/_static/install-selector.js +++ b/docs/cuopt/source/_static/install-selector.js @@ -246,6 +246,9 @@ /* Java has no pip/conda package; the Docker images already contain cuopt.jar. */ java: { container: CONTAINER_CUOPT_LIB, + /* Marker only -- getCommand() builds the actual pom.xml snippet, since it + also depends on arch (classifier suffix), not just release/cuda. */ + maven: { stable: { cu12: true, cu13: true }, nightly: { cu12: true, cu13: true } }, }, }; @@ -254,7 +257,7 @@ c: ["pip", "conda", "container"], server: ["pip", "conda", "container"], cli: ["pip", "conda", "container"], - java: ["container"], + java: ["container", "maven"], }; function getSelectedValue(name) { @@ -326,6 +329,29 @@ "# Run the container:\n" + runLine; } + } else if (method === "maven") { + var mvnVersion = release === "nightly" ? V_NEXT + ".0-SNAPSHOT" : V; + var arch = getSelectedValue("cuopt-arch") || "amd64"; + var classifier = (cuda || "cu12").replace("cu", "cuda") + (arch === "arm64" ? "-arm64" : ""); + var repoBlock = + release === "nightly" + ? "\n" + + " \n" + + " sonatype-snapshots\n" + + " https://central.sonatype.com/repository/maven-snapshots\n" + + " false\n" + + " true\n" + + " \n" + + "\n\n" + : ""; + cmd = + repoBlock + + "\n" + + " com.nvidia.cuopt\n" + + " cuopt\n" + + " " + mvnVersion + "\n" + + " " + classifier + "\n" + + ""; } else { var key = data[release].cu12 && data[release].cu13 ? cuda : "default"; cmd = data[release][key] || data[release].cu12 || data[release].cu13 || data[release].default || ""; @@ -378,6 +404,10 @@ if (registryRow) { registryRow.style.display = method === "container" ? "table-row" : "none"; } + var archRow = document.getElementById("cuopt-arch-row"); + if (archRow) { + archRow.style.display = iface === "java" && method === "maven" ? "table-row" : "none"; + } updateOutput(); } @@ -415,6 +445,7 @@ '' + '' + '' + + '' + '' + 'Release' + '' + @@ -432,13 +463,17 @@ '' + '' + '' + + 'Arch' + + '' + + '' + + '' + "" + '
' + '' + '
' + "
"; - ["cuopt-iface", "cuopt-method", "cuopt-release", "cuopt-cuda", "cuopt-variant", "cuopt-registry"].forEach( + ["cuopt-iface", "cuopt-method", "cuopt-release", "cuopt-cuda", "cuopt-variant", "cuopt-registry", "cuopt-arch"].forEach( function (name) { var inputs = document.querySelectorAll('input[name="' + name + '"]'); inputs.forEach(function (input) { From 1e7667c2a4ddde4aa927105ceaba754698e29776 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 09:25:32 -0500 Subject: [PATCH 11/14] docs(c): add libcuopt component installs to the selector - New Component row (Full/Client/Mathopt/Routing) for C's pip/conda - Client has no cu12/cu13 split (no CUDA/rmm link); mathopt/routing do - Umbrella libcuopt still works unchanged on both pip and conda -- this is for callers who only want one piece - Mention it in cuopt-c/quick-start.rst --- docs/cuopt/source/_static/install-selector.js | 86 +++++++++++++++++-- docs/cuopt/source/cuopt-c/quick-start.rst | 2 + 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/docs/cuopt/source/_static/install-selector.js b/docs/cuopt/source/_static/install-selector.js index 7463794a2f..b473ec534b 100644 --- a/docs/cuopt/source/_static/install-selector.js +++ b/docs/cuopt/source/_static/install-selector.js @@ -36,6 +36,20 @@ var V_CONDA_NEXT = nextMajor + "." + (nextMinor < 10 ? "0" : "") + nextMinor; var V_NEXT = nextMajor + "." + nextMinor; + function pipInstall(pkg, cudaSuffix, version, nightly) { + var name = pkg + (cudaSuffix ? "-" + cudaSuffix : ""); + var flags = nightly + ? "--pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/" + : "--extra-index-url=https://pypi.nvidia.com"; + return "pip install " + flags + " '" + name + "==" + version + ".*'"; + } + + function condaInstall(pkg, version, cudaVersion, nightly) { + var cmd = "conda install -c " + (nightly ? "rapidsai-nightly" : "rapidsai") + + " -c conda-forge -c nvidia " + pkg + "=" + version + ".*"; + return cudaVersion ? cmd + " cuda-version=" + cudaVersion : cmd; + } + /* Shared Docker image lines: same tags are typically published to Docker Hub and NGC */ var CONTAINER_CUOPT_LIB = { stable: { @@ -252,6 +266,42 @@ }, }; + /* Single-component C API installs: libcuopt still ships everything standalone (pip) or + depends on all three (conda), so these are for callers who want just one piece. Client + links no CUDA/rmm, so it has one universal package, no cu12/cu13 split. */ + var LIBCUOPT_COMPONENTS = { + client: { + pip: { + stable: { default: pipInstall("libcuopt-client", "", V, false) }, + nightly: { default: pipInstall("libcuopt-client", "", V_NEXT, true) }, + }, + conda: { + stable: { default: condaInstall("libcuopt-client", V_CONDA, "", false) }, + nightly: { default: condaInstall("libcuopt-client", V_CONDA_NEXT, "", true) }, + }, + }, + mathopt: { + pip: { + stable: { cu12: pipInstall("libcuopt-mathopt", "cu12", V, false), cu13: pipInstall("libcuopt-mathopt", "cu13", V, false) }, + nightly: { cu12: pipInstall("libcuopt-mathopt", "cu12", V_NEXT, true), cu13: pipInstall("libcuopt-mathopt", "cu13", V_NEXT, true) }, + }, + conda: { + stable: { cu12: condaInstall("libcuopt-mathopt", V_CONDA, "12.9", false), cu13: condaInstall("libcuopt-mathopt", V_CONDA, "13.0", false) }, + nightly: { cu12: condaInstall("libcuopt-mathopt", V_CONDA_NEXT, "12.9", true), cu13: condaInstall("libcuopt-mathopt", V_CONDA_NEXT, "13.0", true) }, + }, + }, + routing: { + pip: { + stable: { cu12: pipInstall("libcuopt-routing", "cu12", V, false), cu13: pipInstall("libcuopt-routing", "cu13", V, false) }, + nightly: { cu12: pipInstall("libcuopt-routing", "cu12", V_NEXT, true), cu13: pipInstall("libcuopt-routing", "cu13", V_NEXT, true) }, + }, + conda: { + stable: { cu12: condaInstall("libcuopt-routing", V_CONDA, "12.9", false), cu13: condaInstall("libcuopt-routing", V_CONDA, "13.0", false) }, + nightly: { cu12: condaInstall("libcuopt-routing", V_CONDA_NEXT, "12.9", true), cu13: condaInstall("libcuopt-routing", V_CONDA_NEXT, "13.0", true) }, + }, + }, + }; + var SUPPORTED_METHODS = { python: ["pip", "conda", "container"], c: ["pip", "conda", "container"], @@ -265,10 +315,18 @@ return el ? el.value : ""; } - function hasCudaVariants(iface, method) { - var d = COMMANDS[iface] && COMMANDS[iface][method]; - if (!d || !d.stable) return false; - return !!(d.stable.cu12 && d.stable.cu13); + /* component is only meaningful for iface "c" + method pip/conda; "full" means the + regular COMMANDS table, anything else looks up LIBCUOPT_COMPONENTS instead. */ + function resolveData(iface, method, component) { + if (component && component !== "full") { + return LIBCUOPT_COMPONENTS[component] && LIBCUOPT_COMPONENTS[component][method]; + } + return COMMANDS[iface] && COMMANDS[iface][method]; + } + + function hasCudaVariants(data) { + if (!data || !data.stable) return false; + return !!(data.stable.cu12 && data.stable.cu13); } function getCommand() { @@ -276,15 +334,18 @@ var method = getSelectedValue("cuopt-method"); var release = getSelectedValue("cuopt-release"); var cuda = getSelectedValue("cuopt-cuda"); + var component = iface === "c" ? (getSelectedValue("cuopt-component") || "full") : "full"; /* CLI uses libcuopt (c) install; cuopt_cli is shipped with libcuopt. */ if (iface === "cli") { iface = "c"; release = "stable"; cuda = "cu12"; + component = "full"; } + if (method === "container") component = "full"; - var data = COMMANDS[iface] && COMMANDS[iface][method]; + var data = resolveData(iface, method, component); if (!data || !data[release]) return ""; var cmd = ""; @@ -390,10 +451,11 @@ var releaseRow = document.getElementById("cuopt-release-row"); var releaseVisible = iface !== "cli"; var ifaceForVariants = iface === "cli" ? "c" : iface; + var component = iface === "c" && method !== "container" ? (getSelectedValue("cuopt-component") || "full") : "full"; var showCuda = releaseVisible && (method === "pip" || method === "conda" || method === "container") && - hasCudaVariants(ifaceForVariants, method); + hasCudaVariants(resolveData(ifaceForVariants, method, component)); cudaRow.style.display = showCuda ? "table-row" : "none"; releaseRow.style.display = releaseVisible ? "table-row" : "none"; var variantRow = document.getElementById("cuopt-variant-row"); @@ -408,6 +470,10 @@ if (archRow) { archRow.style.display = iface === "java" && method === "maven" ? "table-row" : "none"; } + var componentRow = document.getElementById("cuopt-component-row"); + if (componentRow) { + componentRow.style.display = iface === "c" && (method === "pip" || method === "conda") ? "table-row" : "none"; + } updateOutput(); } @@ -447,6 +513,12 @@ '' + '' + '' + + 'Component' + + '' + + '' + + '' + + '' + + '' + 'Release' + '' + '' + @@ -473,7 +545,7 @@ '
' + ""; - ["cuopt-iface", "cuopt-method", "cuopt-release", "cuopt-cuda", "cuopt-variant", "cuopt-registry", "cuopt-arch"].forEach( + ["cuopt-iface", "cuopt-method", "cuopt-release", "cuopt-cuda", "cuopt-variant", "cuopt-registry", "cuopt-arch", "cuopt-component"].forEach( function (name) { var inputs = document.querySelectorAll('input[name="' + name + '"]'); inputs.forEach(function (input) { diff --git a/docs/cuopt/source/cuopt-c/quick-start.rst b/docs/cuopt/source/cuopt-c/quick-start.rst index e7a489204a..d1a35faf21 100644 --- a/docs/cuopt/source/cuopt-c/quick-start.rst +++ b/docs/cuopt/source/cuopt-c/quick-start.rst @@ -13,4 +13,6 @@ Choose your install method below; the selector is pre-set for the C API (libcuop .. install-selector:: :default-iface: c +For pip/Conda, the selector's Component option installs a single piece of ``libcuopt`` (client, mathopt, or routing) instead of the full library, for callers who only need one. + Please visit examples under each section to learn how to use the cuOpt C API. From 126f1e2ee39b65b934399eeeee20b541a2922c90 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 09:48:00 -0500 Subject: [PATCH 12/14] docs(java): fix CUDA row hidden for Maven, default it to cu13 - showCuda's method check omitted "maven" -- the row (and the ability to pick cu12/cu13 at all) was invisible even though the classifier depends on it - Default to cu13 on entering the Maven method, matching the published jar's own unclassified-primary default (see PR #1970's assemble_maven_repo.sh); doesn't override an explicit pick made while still in that method --- docs/cuopt/source/_static/install-selector.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/cuopt/source/_static/install-selector.js b/docs/cuopt/source/_static/install-selector.js index b473ec534b..86341d6923 100644 --- a/docs/cuopt/source/_static/install-selector.js +++ b/docs/cuopt/source/_static/install-selector.js @@ -429,10 +429,21 @@ copyBtn.style.display = cmd ? "inline-flex" : "none"; } + var lastMethod = ""; + function updateVisibility() { var method = getSelectedValue("cuopt-method"); var iface = getSelectedValue("cuopt-iface"); var allowed = SUPPORTED_METHODS[iface] || []; + + /* The published Maven jar's own unclassified default is cuda13 (see + assemble_maven_repo.sh); default the CUDA radio to match on entering + this method, without overriding an explicit choice made while still in it. */ + if (method === "maven" && lastMethod !== "maven") { + var cu13 = document.querySelector('input[name="cuopt-cuda"][value="cu13"]'); + if (cu13) cu13.checked = true; + } + lastMethod = method; var methodInputs = document.querySelectorAll('input[name="cuopt-method"]'); methodInputs.forEach(function (input) { var enabled = allowed.indexOf(input.value) !== -1; @@ -454,7 +465,7 @@ var component = iface === "c" && method !== "container" ? (getSelectedValue("cuopt-component") || "full") : "full"; var showCuda = releaseVisible && - (method === "pip" || method === "conda" || method === "container") && + (method === "pip" || method === "conda" || method === "container" || method === "maven") && hasCudaVariants(resolveData(ifaceForVariants, method, component)); cudaRow.style.display = showCuda ? "table-row" : "none"; releaseRow.style.display = releaseVisible ? "table-row" : "none"; From 1a9a250c7537e0d806aa5cddc1c75873d5bd85d0 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 11:02:17 -0500 Subject: [PATCH 13/14] docs(java): convert inline examples to real, compiled, run-verified files - Add examples/*.java + sample.mps under quick-start, convex, and mip, matching the Python/C download+literalinclude convention (previously all inline code-blocks, no backing files) - Every example built and run for real against a fresh 26.10.0 build (java/cuopt/build/native + target/cuopt-*.jar), fixing two real bugs found in the process: - QuadraticConstraint/SemiContinuous had zero linear constraint rows; cuOptCreateProblem currently requires at least one, so both needed a real (possibly non-binding) linear constraint added - IncumbentCallback's Problem.fromIncumbent call referenced a nonexistent "z" variable; fixed to the problem's actual variables - MIP Starts/Incumbent Callback/LP Relaxation are excerpted via start-after/end-before from complete, real, standalone files instead of prose fragments with no backing program --- .../cuopt-java/convex/convex-examples.rst | 116 +++++++--------- .../convex/examples/MpsRoundtrip.java | 14 ++ .../convex/examples/QuadraticConstraint.java | 28 ++++ .../cuopt-java/convex/examples/SimpleLp.java | 32 +++++ .../cuopt-java/convex/examples/SimpleQp.java | 30 ++++ .../cuopt-java/convex/examples/sample.mps | 13 ++ .../source/cuopt-java/examples/LpExample.java | 25 ++++ .../cuopt-java/examples/MipExample.java | 21 +++ .../cuopt-java/examples/QpQuickstart.java | 21 +++ .../source/cuopt-java/examples/SmokeTest.java | 22 +++ .../mip/examples/IncumbentCallback.java | 49 +++++++ .../cuopt-java/mip/examples/LpRelaxation.java | 26 ++++ .../cuopt-java/mip/examples/MipStarts.java | 41 ++++++ .../mip/examples/SemiContinuous.java | 24 ++++ .../cuopt-java/mip/examples/SimpleMip.java | 33 +++++ .../source/cuopt-java/mip/mip-examples.rst | 131 +++++++----------- docs/cuopt/source/cuopt-java/quick-start.rst | 89 ++++-------- 17 files changed, 499 insertions(+), 216 deletions(-) create mode 100644 docs/cuopt/source/cuopt-java/convex/examples/MpsRoundtrip.java create mode 100644 docs/cuopt/source/cuopt-java/convex/examples/QuadraticConstraint.java create mode 100644 docs/cuopt/source/cuopt-java/convex/examples/SimpleLp.java create mode 100644 docs/cuopt/source/cuopt-java/convex/examples/SimpleQp.java create mode 100644 docs/cuopt/source/cuopt-java/convex/examples/sample.mps create mode 100644 docs/cuopt/source/cuopt-java/examples/LpExample.java create mode 100644 docs/cuopt/source/cuopt-java/examples/MipExample.java create mode 100644 docs/cuopt/source/cuopt-java/examples/QpQuickstart.java create mode 100644 docs/cuopt/source/cuopt-java/examples/SmokeTest.java create mode 100644 docs/cuopt/source/cuopt-java/mip/examples/IncumbentCallback.java create mode 100644 docs/cuopt/source/cuopt-java/mip/examples/LpRelaxation.java create mode 100644 docs/cuopt/source/cuopt-java/mip/examples/MipStarts.java create mode 100644 docs/cuopt/source/cuopt-java/mip/examples/SemiContinuous.java create mode 100644 docs/cuopt/source/cuopt-java/mip/examples/SimpleMip.java diff --git a/docs/cuopt/source/cuopt-java/convex/convex-examples.rst b/docs/cuopt/source/cuopt-java/convex/convex-examples.rst index 84b6e90425..3f8a59fab5 100644 --- a/docs/cuopt/source/cuopt-java/convex/convex-examples.rst +++ b/docs/cuopt/source/cuopt-java/convex/convex-examples.rst @@ -11,32 +11,20 @@ Simple Linear Programming The high-level API uses fluent expressions and explicit comparison methods. -.. code-block:: java +:download:`SimpleLp.java ` - import com.nvidia.cuopt.mathematicaloptimization.*; - - try (Problem problem = new Problem("simple-lp")) { - Variable x = problem.addVariable( - 0.0, Double.POSITIVE_INFINITY, 1.0, - VariableType.CONTINUOUS, "x"); - Variable y = problem.addVariable( - 0.0, Double.POSITIVE_INFINITY, 1.0, - VariableType.CONTINUOUS, "y"); - - problem.addConstraint( - LinearExpression.of(x).plus(y).ge(10.0), "demand"); - problem.setObjective( - LinearExpression.of(x).plus(y), ObjectiveSense.MINIMIZE); - - try (SolverSettings settings = new SolverSettings() - .setMethod(SolverMethod.PDLP); - Solution solution = problem.solve(settings)) { - System.out.println("Status: " + solution.getTerminationStatus()); - System.out.println("x = " + x.getValue()); - System.out.println("y = " + y.getValue()); - System.out.println("Objective = " + solution.getPrimalObjective()); - } - } +.. literalinclude:: examples/SimpleLp.java + :language: java + :linenos: + +Example Response: + +.. code-block:: text + + Status: OPTIMAL + x = 0.0 + y = 10.0 + Objective = 10.0 ``Problem.solve`` populates the ``Variable`` and ``Constraint`` objects after the solve. The solution object remains available for detailed native results @@ -47,28 +35,19 @@ Simple Quadratic Programming Quadratic objectives combine quadratic, linear, and constant terms: -.. code-block:: java +:download:`SimpleQp.java ` - try (Problem problem = new Problem("simple-qp")) { - Variable x = problem.addVariable(0.0, 10.0, 0.0, VariableType.CONTINUOUS, "x"); - Variable y = problem.addVariable(0.0, 10.0, 0.0, VariableType.CONTINUOUS, "y"); - - QuadraticExpression objective = QuadraticExpression - .of(x, x, 1.0) - .plus(y, y, 1.0) - .plus(LinearExpression.of(x).times(-1.0)) - .plus(LinearExpression.of(y).times(-1.0)); - - problem.addConstraint( - LinearExpression.of(x).plus(y).eq(1.0), "sum"); - problem.setObjective(objective, ObjectiveSense.MINIMIZE); - - try (Solution solution = problem.solve()) { - System.out.println("x = " + x.getValue()); - System.out.println("y = " + y.getValue()); - System.out.println("Objective = " + solution.getPrimalObjective()); - } - } +.. literalinclude:: examples/SimpleQp.java + :language: java + :linenos: + +Example Response: + +.. code-block:: text + + x = 0.5 + y = 0.5 + Objective = -0.5 For QP solutions, ``getDualObjective`` is available when the solver returns it, and variable and constraint values are read from the model through @@ -78,25 +57,16 @@ and variable and constraint values are read from the model through Quadratic Constraints --------------------- -Quadratic constraints can be added directly to a ``Problem``: - -.. code-block:: java - - try (Problem problem = new Problem("quadratic-constraint")) { - Variable x = problem.addVariable(0.0, 10.0, 1.0, VariableType.CONTINUOUS, "x"); - Variable y = problem.addVariable(0.0, 10.0, 1.0, VariableType.CONTINUOUS, "y"); +Quadratic constraints can be added directly to a ``Problem``. As of this +writing, ``cuOptCreateProblem`` requires at least one linear constraint row, +so a purely quadratically-constrained model needs a (possibly non-binding) +linear constraint too: - QuadraticExpression radius = QuadraticExpression - .of(x, x, 1.0) - .plus(y, y, 1.0); - problem.addConstraint(radius.le(4.0), "radius"); - problem.setObjective( - LinearExpression.of(x).plus(y), ObjectiveSense.MAXIMIZE); +:download:`QuadraticConstraint.java ` - try (Solution solution = problem.solve()) { - System.out.println(solution.getTerminationStatus()); - } - } +.. literalinclude:: examples/QuadraticConstraint.java + :language: java + :linenos: Only ``LE`` and ``GE`` quadratic constraints are supported; ``QuadraticExpression`` does not expose an ``eq`` method. @@ -106,12 +76,22 @@ Reading and Writing MPS/QPS ``Problem`` exposes both extension-dispatch and direct MPS entry points: -.. code-block:: java +:download:`MpsRoundtrip.java ` and +:download:`sample.mps ` - try (Problem problem = Problem.read("problem.mps")) { - System.out.println("Variables: " + problem.getNumVariables()); - problem.write("roundtrip.mps"); - } +.. literalinclude:: examples/MpsRoundtrip.java + :language: java + :linenos: + +Example Response: + +.. code-block:: text + + Variables: 2 + +Fixed-format parsing is also available: + +.. code-block:: java try (Problem fixed = Problem.read("fixed-format.mps", true)) { // Use fixed-format parsing explicitly. diff --git a/docs/cuopt/source/cuopt-java/convex/examples/MpsRoundtrip.java b/docs/cuopt/source/cuopt-java/convex/examples/MpsRoundtrip.java new file mode 100644 index 0000000000..8cb9af1ab6 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/convex/examples/MpsRoundtrip.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class MpsRoundtrip { + public static void main(String[] args) throws Exception { + try (Problem problem = Problem.read("sample.mps")) { + System.out.println("Variables: " + problem.getNumVariables()); + problem.write("roundtrip.mps"); + } + } +} diff --git a/docs/cuopt/source/cuopt-java/convex/examples/QuadraticConstraint.java b/docs/cuopt/source/cuopt-java/convex/examples/QuadraticConstraint.java new file mode 100644 index 0000000000..bcae7d285b --- /dev/null +++ b/docs/cuopt/source/cuopt-java/convex/examples/QuadraticConstraint.java @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class QuadraticConstraint { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("quadratic-constraint")) { + Variable x = problem.addVariable(0.0, 10.0, 1.0, VariableType.CONTINUOUS, "x"); + Variable y = problem.addVariable(0.0, 10.0, 1.0, VariableType.CONTINUOUS, "y"); + + // cuOptCreateProblem currently requires at least one linear constraint row. + problem.addConstraint(LinearExpression.of(x).plus(y).le(15.0), "budget"); + + QuadraticExpression radius = QuadraticExpression + .of(x, x, 1.0) + .plus(y, y, 1.0); + problem.addConstraint(radius.le(4.0), "radius"); + problem.setObjective( + LinearExpression.of(x).plus(y), ObjectiveSense.MAXIMIZE); + + try (Solution solution = problem.solve()) { + System.out.println(solution.getTerminationStatus()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/convex/examples/SimpleLp.java b/docs/cuopt/source/cuopt-java/convex/examples/SimpleLp.java new file mode 100644 index 0000000000..0273281b69 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/convex/examples/SimpleLp.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class SimpleLp { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("simple-lp")) { + Variable x = problem.addVariable( + 0.0, Double.POSITIVE_INFINITY, 1.0, + VariableType.CONTINUOUS, "x"); + Variable y = problem.addVariable( + 0.0, Double.POSITIVE_INFINITY, 1.0, + VariableType.CONTINUOUS, "y"); + + problem.addConstraint( + LinearExpression.of(x).plus(y).ge(10.0), "demand"); + problem.setObjective( + LinearExpression.of(x).plus(y), ObjectiveSense.MINIMIZE); + + try (SolverSettings settings = new SolverSettings() + .setMethod(SolverMethod.PDLP); + Solution solution = problem.solve(settings)) { + System.out.println("Status: " + solution.getTerminationStatus()); + System.out.println("x = " + x.getValue()); + System.out.println("y = " + y.getValue()); + System.out.println("Objective = " + solution.getPrimalObjective()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/convex/examples/SimpleQp.java b/docs/cuopt/source/cuopt-java/convex/examples/SimpleQp.java new file mode 100644 index 0000000000..93a08c70d4 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/convex/examples/SimpleQp.java @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class SimpleQp { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("simple-qp")) { + Variable x = problem.addVariable(0.0, 10.0, 0.0, VariableType.CONTINUOUS, "x"); + Variable y = problem.addVariable(0.0, 10.0, 0.0, VariableType.CONTINUOUS, "y"); + + QuadraticExpression objective = QuadraticExpression + .of(x, x, 1.0) + .plus(y, y, 1.0) + .plus(LinearExpression.of(x).times(-1.0)) + .plus(LinearExpression.of(y).times(-1.0)); + + problem.addConstraint( + LinearExpression.of(x).plus(y).eq(1.0), "sum"); + problem.setObjective(objective, ObjectiveSense.MINIMIZE); + + try (Solution solution = problem.solve()) { + System.out.println("x = " + x.getValue()); + System.out.println("y = " + y.getValue()); + System.out.println("Objective = " + solution.getPrimalObjective()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/convex/examples/sample.mps b/docs/cuopt/source/cuopt-java/convex/examples/sample.mps new file mode 100644 index 0000000000..95d342250c --- /dev/null +++ b/docs/cuopt/source/cuopt-java/convex/examples/sample.mps @@ -0,0 +1,13 @@ +NAME good-1 +ROWS + N COST + L ROW1 + L ROW2 +COLUMNS + VAR1 COST -0.2 + VAR1 ROW1 3 ROW2 2.7 + VAR2 COST 0.1 + VAR2 ROW1 4 ROW2 10.1 +RHS + RHS1 ROW1 5.4 ROW2 4.9 +ENDATA diff --git a/docs/cuopt/source/cuopt-java/examples/LpExample.java b/docs/cuopt/source/cuopt-java/examples/LpExample.java new file mode 100644 index 0000000000..cad6c76fa3 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/examples/LpExample.java @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class LpExample { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("simple")) { + Variable x = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, + VariableType.CONTINUOUS, "x"); + Variable y = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, + VariableType.CONTINUOUS, "y"); + + problem.addConstraint(LinearExpression.of(x).plus(y).ge(1.0), "c0"); + problem.setObjective(LinearExpression.of(x).plus(y), ObjectiveSense.MINIMIZE); + + try (SolverSettings settings = new SolverSettings().setMethod(SolverMethod.PDLP); + Solution solution = problem.solve(settings)) { + System.out.println(solution.getTerminationStatus()); + System.out.println(solution.getPrimalObjective()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/examples/MipExample.java b/docs/cuopt/source/cuopt-java/examples/MipExample.java new file mode 100644 index 0000000000..743f06c3a7 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/examples/MipExample.java @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class MipExample { + public static void main(String[] args) throws Exception { + 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)) { + System.out.println(solution.getMIPGap()); + System.out.println(solution.getSolutionBound()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/examples/QpQuickstart.java b/docs/cuopt/source/cuopt-java/examples/QpQuickstart.java new file mode 100644 index 0000000000..873f898529 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/examples/QpQuickstart.java @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class QpQuickstart { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("quadratic")) { + Variable x = problem.addVariable(0.0, 10.0, 0.0, VariableType.CONTINUOUS, "x"); + Variable y = problem.addVariable(0.0, 10.0, 0.0, VariableType.CONTINUOUS, "y"); + problem.addConstraint(LinearExpression.of(x).plus(y).ge(5.0)); + problem.setObjective( + QuadraticExpression.of(x, x, 1.0).plus(y, y, 4.0), + ObjectiveSense.MINIMIZE); + try (Solution solution = problem.solve()) { + System.out.println(solution.getPrimalObjective()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/examples/SmokeTest.java b/docs/cuopt/source/cuopt-java/examples/SmokeTest.java new file mode 100644 index 0000000000..26465b8535 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/examples/SmokeTest.java @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class SmokeTest { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("smoke-test")) { + Variable x = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, + VariableType.CONTINUOUS, "x"); + Variable y = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, + VariableType.CONTINUOUS, "y"); + problem.addConstraint(LinearExpression.of(x).plus(y).ge(1.0), "c0"); + problem.setObjective(LinearExpression.of(x).plus(y), ObjectiveSense.MINIMIZE); + try (Solution solution = problem.solve()) { + System.out.println(solution.getTerminationStatus()); + System.out.println(solution.getPrimalObjective()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/mip/examples/IncumbentCallback.java b/docs/cuopt/source/cuopt-java/mip/examples/IncumbentCallback.java new file mode 100644 index 0000000000..9b48841874 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/mip/examples/IncumbentCallback.java @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class IncumbentCallback { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("simple-milp")) { + Variable x = problem.addVariable(0.0, 100.0, 3.0, VariableType.INTEGER, "x"); + Variable y = problem.addVariable(0.0, 100.0, 5.0, VariableType.INTEGER, "y"); + problem.addConstraint(LinearExpression.of(x).times(2.0).plus(y).le(8.0), "capacity"); + problem.setObjective( + LinearExpression.of(x).times(3.0).plus(y, 5.0), ObjectiveSense.MAXIMIZE); + + // start-basic-callback + try (SolverSettings settings = new SolverSettings()) { + settings.setMIPCallback( + (incumbent, objective, bound, userData) -> { + System.out.println( + "incumbent objective=" + objective + ", bound=" + bound); + }, + null, + problem.getNumVariables()); + + try (Solution solution = problem.solve(settings)) { + System.out.println("Final status: " + solution.getTerminationStatus()); + } + } + // end-basic-callback + + // start-from-incumbent + try (SolverSettings settings = new SolverSettings()) { + settings.setMIPCallback( + (incumbent, objective, bound, userData) -> { + double[] picked = Problem.fromIncumbent(incumbent, y, x); + System.out.println("y=" + picked[0] + " x=" + picked[1]); + }, + null, + problem.getNumVariables()); + + try (Solution solution = problem.solve(settings)) { + System.out.println("Final status: " + solution.getTerminationStatus()); + } + } + // end-from-incumbent + } + } +} diff --git a/docs/cuopt/source/cuopt-java/mip/examples/LpRelaxation.java b/docs/cuopt/source/cuopt-java/mip/examples/LpRelaxation.java new file mode 100644 index 0000000000..8dce37a8b2 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/mip/examples/LpRelaxation.java @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class LpRelaxation { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("simple-milp")) { + Variable x = problem.addVariable(0.0, 100.0, 3.0, VariableType.INTEGER, "x"); + Variable y = problem.addVariable(0.0, 100.0, 5.0, VariableType.INTEGER, "y"); + problem.addConstraint(LinearExpression.of(x).times(2.0).plus(y).le(8.0), "capacity"); + problem.setObjective( + LinearExpression.of(x).times(3.0).plus(y, 5.0), ObjectiveSense.MAXIMIZE); + + // start-relax + for (Variable variable : problem.getVariables()) { + variable.setVariableType(VariableType.CONTINUOUS); + } + try (Solution solution = problem.solve()) { + System.out.println("LP relaxation objective = " + solution.getPrimalObjective()); + } + // end-relax + } + } +} diff --git a/docs/cuopt/source/cuopt-java/mip/examples/MipStarts.java b/docs/cuopt/source/cuopt-java/mip/examples/MipStarts.java new file mode 100644 index 0000000000..44e02624a4 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/mip/examples/MipStarts.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class MipStarts { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("simple-milp")) { + Variable x = problem.addVariable(0.0, 100.0, 3.0, VariableType.INTEGER, "x"); + Variable y = problem.addVariable(0.0, 100.0, 5.0, VariableType.INTEGER, "y"); + problem.addConstraint(LinearExpression.of(x).times(2.0).plus(y).le(8.0), "capacity"); + problem.setObjective( + LinearExpression.of(x).times(3.0).plus(y, 5.0), ObjectiveSense.MAXIMIZE); + + // start-per-variable + x.setMIPStart(3.0); + y.setMIPStart(2.0); + + try (SolverSettings settings = new SolverSettings(); + Solution solution = problem.solve(settings)) { + System.out.println(solution.getPrimalObjective()); + } + // end-per-variable + + // start-array + try (SolverSettings settings = new SolverSettings()) { + double[] values = new double[problem.getNumVariables()]; + for (Variable variable : problem.getVariables()) { + values[variable.getIndex()] = variable.getMIPStart(); + } + settings.addMIPStart(values); + + try (Solution solution = problem.solve(settings)) { + System.out.println(solution.getPrimalObjective()); + } + } + // end-array + } + } +} diff --git a/docs/cuopt/source/cuopt-java/mip/examples/SemiContinuous.java b/docs/cuopt/source/cuopt-java/mip/examples/SemiContinuous.java new file mode 100644 index 0000000000..191efa94f2 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/mip/examples/SemiContinuous.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class SemiContinuous { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("semi-continuous")) { + Variable production = problem.addVariable( + 10.0, 100.0, 1.0, + VariableType.SEMI_CONTINUOUS, "production"); + + // cuOptCreateProblem currently requires at least one linear constraint row. + problem.addConstraint(LinearExpression.of(production).le(1000.0), "capacity"); + + problem.setObjective(production, ObjectiveSense.MINIMIZE); + + try (Solution solution = problem.solve()) { + System.out.println("production = " + production.getValue()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/mip/examples/SimpleMip.java b/docs/cuopt/source/cuopt-java/mip/examples/SimpleMip.java new file mode 100644 index 0000000000..457e68fd92 --- /dev/null +++ b/docs/cuopt/source/cuopt-java/mip/examples/SimpleMip.java @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import com.nvidia.cuopt.mathematicaloptimization.*; + +public class SimpleMip { + public static void main(String[] args) throws Exception { + try (Problem problem = new Problem("simple-milp")) { + Variable x = problem.addVariable( + 0.0, 100.0, 3.0, VariableType.INTEGER, "x"); + Variable y = problem.addVariable( + 0.0, 100.0, 5.0, VariableType.INTEGER, "y"); + + problem.addConstraint( + LinearExpression.of(x).times(2.0).plus(y).le(8.0), "capacity"); + problem.setObjective( + LinearExpression.of(x).times(3.0).plus(y, 5.0), + ObjectiveSense.MAXIMIZE); + + try (SolverSettings settings = new SolverSettings() + .setSetting(CuOptConstants.CUOPT_TIME_LIMIT, 10.0); + Solution solution = problem.solve(settings)) { + System.out.println("Status: " + solution.getTerminationStatus()); + System.out.println("x = " + x.getValue()); + System.out.println("y = " + y.getValue()); + System.out.println("Objective = " + solution.getPrimalObjective()); + System.out.println("MIP gap = " + solution.getMIPGap()); + System.out.println("Bound = " + solution.getSolutionBound()); + } + } + } +} diff --git a/docs/cuopt/source/cuopt-java/mip/mip-examples.rst b/docs/cuopt/source/cuopt-java/mip/mip-examples.rst index 7c90e520e6..d2a10cf88c 100644 --- a/docs/cuopt/source/cuopt-java/mip/mip-examples.rst +++ b/docs/cuopt/source/cuopt-java/mip/mip-examples.rst @@ -8,33 +8,22 @@ variables, and incumbent callbacks in Java. Simple MIP ---------- -.. code-block:: java - - import com.nvidia.cuopt.mathematicaloptimization.*; - - try (Problem problem = new Problem("simple-milp")) { - Variable x = problem.addVariable( - 0.0, 100.0, 3.0, VariableType.INTEGER, "x"); - Variable y = problem.addVariable( - 0.0, 100.0, 5.0, VariableType.INTEGER, "y"); - - problem.addConstraint( - LinearExpression.of(x).times(2.0).plus(y).le(8.0), "capacity"); - problem.setObjective( - LinearExpression.of(x).times(3.0).plus(y, 5.0), - ObjectiveSense.MAXIMIZE); - - try (SolverSettings settings = new SolverSettings() - .setSetting(CuOptConstants.CUOPT_TIME_LIMIT, 10.0); - Solution solution = problem.solve(settings)) { - System.out.println("Status: " + solution.getTerminationStatus()); - System.out.println("x = " + x.getValue()); - System.out.println("y = " + y.getValue()); - System.out.println("Objective = " + solution.getPrimalObjective()); - System.out.println("MIP gap = " + solution.getMIPGap()); - System.out.println("Bound = " + solution.getSolutionBound()); - } - } +:download:`SimpleMip.java ` + +.. literalinclude:: examples/SimpleMip.java + :language: java + :linenos: + +Example Response: + +.. code-block:: text + + Status: OPTIMAL + x = 0.0 + y = 8.0 + Objective = 40.0 + MIP gap = 0.0 + Bound = 40.0 The MIP solver can return a feasible solution before proving optimality. Use the termination status, MIP gap, and solution bound together when interpreting @@ -45,33 +34,24 @@ Semi-Continuous Variables ``SEMI_CONTINUOUS`` variables are zero or lie within their declared bounds. -.. code-block:: java - - try (Problem problem = new Problem("semi-continuous")) { - Variable production = problem.addVariable( - 10.0, 100.0, 1.0, - VariableType.SEMI_CONTINUOUS, "production"); - problem.setObjective(production, ObjectiveSense.MINIMIZE); +:download:`SemiContinuous.java ` - try (Solution solution = problem.solve()) { - System.out.println("production = " + production.getValue()); - } - } +.. literalinclude:: examples/SemiContinuous.java + :language: java + :linenos: MIP Starts ---------- Set starts on variables when using the high-level ``Problem`` API: -.. code-block:: java +:download:`MipStarts.java ` - x.setMIPStart(3.0); - y.setMIPStart(2.0); - - try (SolverSettings settings = new SolverSettings(); - Solution solution = problem.solve(settings)) { - System.out.println(solution.getPrimalObjective()); - } +.. literalinclude:: examples/MipStarts.java + :language: java + :start-after: // start-per-variable + :end-before: // end-per-variable + :dedent: Setting a start per variable avoids handling the ordering at all, and is the form to prefer. @@ -82,13 +62,11 @@ since it can be called repeatedly while each ``Variable`` holds a single value. Build it from ``getVariables`` so the ordering comes from the problem rather than from you: -.. code-block:: java - - double[] values = new double[problem.getNumVariables()]; - for (Variable variable : problem.getVariables()) { - values[variable.getIndex()] = startFor(variable); - } - settings.addMIPStart(values); +.. literalinclude:: examples/MipStarts.java + :language: java + :start-after: // start-array + :end-before: // end-array + :dedent: MIP starts are currently unsupported with presolve on. @@ -97,21 +75,13 @@ Incumbent Callback Register an incumbent callback before solving: -.. code-block:: java +:download:`IncumbentCallback.java ` - try (SolverSettings settings = new SolverSettings()) { - settings.setMIPCallback( - (incumbent, objective, bound, userData) -> { - System.out.println( - "incumbent objective=" + objective + ", bound=" + bound); - }, - null, - problem.getNumVariables()); - - try (Solution solution = problem.solve(settings)) { - System.out.println("Final status: " + solution.getTerminationStatus()); - } - } +.. literalinclude:: examples/IncumbentCallback.java + :language: java + :start-after: // start-basic-callback + :end-before: // end-basic-callback + :dedent: The callback receives a defensive copy of the incumbent vector, the incumbent objective, the current solution bound, and the user data object. @@ -120,26 +90,21 @@ The vector is in variable-index order. To read specific variables out of it without depending on that order, pass them to ``Problem.fromIncumbent``, which returns their values in the order you ask for: -.. code-block:: java - - settings.setMIPCallback( - (incumbent, objective, bound, userData) -> { - double[] picked = Problem.fromIncumbent(incumbent, z, y, x); - System.out.println("z=" + picked[0] + " y=" + picked[1] + " x=" + picked[2]); - }, - null, - problem.getNumVariables()); +.. literalinclude:: examples/IncumbentCallback.java + :language: java + :start-after: // start-from-incumbent + :end-before: // end-from-incumbent + :dedent: LP Relaxation ------------- Relax the integer variables before solving: -.. code-block:: java +:download:`LpRelaxation.java ` - for (Variable variable : problem.getVariables()) { - variable.setVariableType(VariableType.CONTINUOUS); - } - try (Solution solution = problem.solve()) { - System.out.println("LP relaxation objective = " + solution.getPrimalObjective()); - } +.. literalinclude:: examples/LpRelaxation.java + :language: java + :start-after: // start-relax + :end-before: // end-relax + :dedent: diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index 236b5cf5b3..e31b3978d7 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -62,30 +62,16 @@ Smoke Test ---------- After installation, verify cuOpt Java is working by compiling and running a -minimal LP inside the container: +minimal LP inside the container. + +:download:`SmokeTest.java ` + +.. literalinclude:: examples/SmokeTest.java + :language: java + :linenos: .. code-block:: bash - cat > SmokeTest.java <<'EOF' - import com.nvidia.cuopt.mathematicaloptimization.*; - - public class SmokeTest { - public static void main(String[] args) throws Exception { - try (Problem problem = new Problem("smoke-test")) { - Variable x = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, - VariableType.CONTINUOUS, "x"); - Variable y = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, - VariableType.CONTINUOUS, "y"); - problem.addConstraint(LinearExpression.of(x).plus(y).ge(1.0), "c0"); - problem.setObjective(LinearExpression.of(x).plus(y), ObjectiveSense.MINIMIZE); - try (Solution solution = problem.solve()) { - System.out.println(solution.getTerminationStatus()); - System.out.println(solution.getPrimalObjective()); - } - } - } - } - EOF javac -cp /opt/cuopt/java/cuopt.jar -d . SmokeTest.java java -Dcuopt.native.dir=/opt/cuopt/java -cp /opt/cuopt/java/cuopt.jar:. SmokeTest @@ -103,66 +89,39 @@ A ``Problem`` owns the variables and constraints. Expressions are assembled with methods that return a new expression, and a constraint is formed by comparing one against a bound with ``le``, ``ge`` or ``eq``. -.. code-block:: java - - import com.nvidia.cuopt.mathematicaloptimization.*; +:download:`LpExample.java ` - Problem problem = new Problem("simple"); - Variable x = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, - VariableType.CONTINUOUS, "x"); - Variable y = problem.addVariable(0, Double.POSITIVE_INFINITY, 0, - VariableType.CONTINUOUS, "y"); - - problem.addConstraint(LinearExpression.of(x).plus(y).ge(1.0), "c0"); - problem.setObjective(LinearExpression.of(x).plus(y), ObjectiveSense.MINIMIZE); - - try (SolverSettings settings = new SolverSettings().setMethod(SolverMethod.PDLP); - Solution solution = problem.solve(settings)) { - System.out.println(solution.getTerminationStatus()); - System.out.println(solution.getPrimalObjective()); - } +.. literalinclude:: examples/LpExample.java + :language: java + :linenos: MIP Example ----------- -.. code-block:: java - - Problem problem = new Problem("integer"); - Variable x = problem.addVariable(0, 10, 1.0, VariableType.INTEGER, "x"); - problem.addConstraint(LinearExpression.of(x).ge(1.0)); +:download:`MipExample.java ` - try (SolverSettings settings = new SolverSettings() - .setSetting(CuOptConstants.CUOPT_TIME_LIMIT, 10.0); - Solution solution = problem.solve(settings)) { - System.out.println(solution.getMIPGap()); - System.out.println(solution.getSolutionBound()); - } +.. literalinclude:: examples/MipExample.java + :language: java + :linenos: QP Example ---------- -.. code-block:: java +:download:`QpQuickstart.java ` - try (Problem problem = new Problem("quadratic")) { - Variable x = problem.addVariable(0.0, 10.0, 0.0, VariableType.CONTINUOUS, "x"); - Variable y = problem.addVariable(0.0, 10.0, 0.0, VariableType.CONTINUOUS, "y"); - problem.addConstraint(LinearExpression.of(x).plus(y).ge(5.0)); - problem.setObjective( - QuadraticExpression.of(x, x, 1.0).plus(y, y, 4.0), - ObjectiveSense.MINIMIZE); - try (Solution solution = problem.solve()) { - System.out.println(solution.getPrimalObjective()); - } - } +.. literalinclude:: examples/QpQuickstart.java + :language: java + :linenos: MPS I/O ------- -.. code-block:: java +:download:`MpsRoundtrip.java ` and +:download:`sample.mps ` - try (Problem problem = Problem.read("problem.mps")) { - problem.write("roundtrip.mps"); - } +.. literalinclude:: convex/examples/MpsRoundtrip.java + :language: java + :linenos: Lifecycle --------- From 8007ae824e99291fd55fa1e49175344808f6ab57 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 29 Sep 2026 13:51:06 -0500 Subject: [PATCH 14/14] docs(java): turn the CUDA runtime library requirement into a note with commands Was prose describing a package-name pattern; now an admonition with copy-paste apt-get/dnf commands for the cuda-libraries package. --- docs/cuopt/source/cuopt-java/quick-start.rst | 28 +++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/cuopt/source/cuopt-java/quick-start.rst b/docs/cuopt/source/cuopt-java/quick-start.rst index e31b3978d7..e1ae51a0b9 100644 --- a/docs/cuopt/source/cuopt-java/quick-start.rst +++ b/docs/cuopt/source/cuopt-java/quick-start.rst @@ -46,15 +46,25 @@ no ``cuopt.native.dir`` is required: cuda12
-The embedded libraries do not include the CUDA toolkit's own math libraries -(``libcublas``, ``libcusolver``, etc.) — those must already be present on the -target system. Loading the jar on a system without them fails with an -``UnsatisfiedLinkError`` naming the missing CUDA library. An -``nvidia/cuda:*-runtime-*`` base image satisfies this without installing -cuOpt itself; outside Docker, install the matching -``cuda-libraries--`` package (e.g. ``cuda-libraries-12-9``) from -`NVIDIA's CUDA repository `_ via -``apt-get`` or ``dnf`` instead of the full CUDA toolkit. +.. note:: + + The embedded libraries do not include the CUDA toolkit's own math libraries + (``libcublas``, ``libcusolver``, etc.) — install them separately, or use an + ``nvidia/cuda:*-runtime-*`` base image instead, which already has them + without installing cuOpt itself. Loading the jar without them fails with an + ``UnsatisfiedLinkError`` naming the missing CUDA library. + + .. code-block:: bash + + # Debian/Ubuntu (with NVIDIA's apt repo already configured) + sudo apt-get install cuda-libraries-12-9 + + # RHEL/Rocky/Fedora (with NVIDIA's dnf repo already configured) + sudo dnf install cuda-libraries-12-9 + + ``cuda-libraries`` is much lighter than the full CUDA toolkit. See + `NVIDIA's CUDA repository setup `_ + if the repo isn't configured yet. Building from source is covered in ``java/cuopt/README.md``.