diff --git a/docs/cuopt/source/_static/install-selector.js b/docs/cuopt/source/_static/install-selector.js
index ceeff29e65..86341d6923 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: {
@@ -243,6 +257,49 @@
},
},
},
+ /* 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 } },
+ },
+ };
+
+ /* 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 = {
@@ -250,6 +307,7 @@
c: ["pip", "conda", "container"],
server: ["pip", "conda", "container"],
cli: ["pip", "conda", "container"],
+ java: ["container", "maven"],
};
function getSelectedValue(name) {
@@ -257,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() {
@@ -268,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 = "";
@@ -321,6 +390,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 || "";
@@ -337,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;
@@ -359,10 +462,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);
+ (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";
var variantRow = document.getElementById("cuopt-variant-row");
@@ -373,6 +477,14 @@
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";
+ }
+ var componentRow = document.getElementById("cuopt-component-row");
+ if (componentRow) {
+ componentRow.style.display = iface === "c" && (method === "pip" || method === "conda") ? "table-row" : "none";
+ }
updateOutput();
}
@@ -404,11 +516,19 @@
'' +
'' +
'' +
+ '' +
'' +
'
| Method | ' +
'' +
'' +
'' +
+ '' +
+ ' |
' +
+ '| Component | ' +
+ '' +
+ '' +
+ '' +
+ '' +
' |
' +
'| Release | ' +
'' +
@@ -426,13 +546,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", "cuopt-component"].forEach(
function (name) {
var inputs = document.querySelectorAll('input[name="' + name + '"]');
inputs.forEach(function (input) {
@@ -444,7 +568,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/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/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.
diff --git a/docs/cuopt/source/cuopt-java/convex/convex-examples.rst b/docs/cuopt/source/cuopt-java/convex/convex-examples.rst
index 371971aa79..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()
- .setSetting(CuOptConstants.CUOPT_METHOD, SolverMethod.PDLP.nativeValue());
- 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..71be9e6c45
--- /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()
+ .setSetting(CuOptConstants.CUOPT_METHOD, SolverMethod.PDLP.nativeValue());
+ 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..330a76aa31
--- /dev/null
+++ b/docs/cuopt/source/cuopt-java/examples/LpExample.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 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()
+ .setSetting(CuOptConstants.CUOPT_METHOD, SolverMethod.PDLP.nativeValue());
+ 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 e1dc18452f..e1ae51a0b9 100644
--- a/docs/cuopt/source/cuopt-java/quick-start.rst
+++ b/docs/cuopt/source/cuopt-java/quick-start.rst
@@ -1,82 +1,96 @@
-Java Quick Start
-================
+Java Quickstart Guide
+=====================
-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.
+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.
-Requirements
-------------
+Installation
+============
-The Java module requires:
+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.
-* 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.
+.. install-selector::
+ :default-iface: java
-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``.
+Using the Maven Artifact
+-------------------------
-.. code-block:: bash
+``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:
- 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
+.. code-block:: xml
-This builds ``java/cuopt/build/native/libcuopt_jni.so``. Java is intentionally
-not part of the default cuOpt build.
+
+
+ sonatype-snapshots
+ https://central.sonatype.com/repository/maven-snapshots
+ false
+ true
+
+
-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.
+
+ com.nvidia.cuopt
+ cuopt
+ 26.10.0-SNAPSHOT
+ cuda12
+
-Native Loading
---------------
+.. note::
-At runtime the bindings load ``libcuopt_jni``. For local development, point Java
-at the directory containing the built native library:
+ 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
+ .. 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
+ # Debian/Ubuntu (with NVIDIA's apt repo already configured)
+ sudo apt-get install cuda-libraries-12-9
-The helper script combines the native build and Maven test steps:
+ # RHEL/Rocky/Fedora (with NVIDIA's dnf repo already configured)
+ sudo dnf install cuda-libraries-12-9
-.. code-block:: bash
+ ``cuda-libraries`` is much lighter than the full CUDA toolkit. See
+ `NVIDIA's CUDA repository setup `_
+ if the repo isn't configured yet.
- 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
+Building from source is covered in ``java/cuopt/README.md``.
-To run one test class, pass its Maven property to the helper:
+Smoke Test
+----------
-.. code-block:: bash
+After installation, verify cuOpt Java is working by compiling and running a
+minimal LP inside the container.
- bash scripts/test.sh -Dtest=ProblemIntegrationTest
+:download:`SmokeTest.java `
-Application code can use the same property:
+.. literalinclude:: examples/SmokeTest.java
+ :language: java
+ :linenos:
.. code-block:: bash
- java -Dcuopt.native.dir=/path/to/java/cuopt/build/native ...
+ 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:
-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.
+.. code-block:: text
+
+ OPTIMAL
+ 1.0
LP Example
----------
@@ -85,67 +99,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.*;
-
- 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");
+:download:`LpExample.java `
- 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()
- .setSetting(CuOptConstants.CUOPT_METHOD, SolverMethod.PDLP.nativeValue());
- 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
---------
diff --git a/docs/cuopt/source/install.rst b/docs/cuopt/source/install.rst
index 404d7361f8..c037ca6ce6 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` (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`
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.