diff --git a/.coderabbit.yaml b/.coderabbit.yaml index bd3f2ae279..5a239204f7 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -59,7 +59,7 @@ reviews: - Consistency: version numbers, parameter types, and terminology match code - Missing docs: if the PR changes public APIs without updating docs, flag as HIGH - When code changes affect docs, suggest specific files (e.g. docs/cuopt/source/*.rst) + When code changes affect docs, suggest specific files (e.g. fern/docs/pages/**/*.mdx) and recommend documenting performance characteristics, GPU requirements, or tolerances. - path: "cpp/include/cuopt/**/*" diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c11938346a..a44ea5120c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -221,13 +221,11 @@ jobs: uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main with: build_type: ${{ inputs.build_type || 'branch' }} - node_type: "gpu-l4-latest-1" + node_type: "cpu4" sha: ${{ inputs.sha }} branch: ${{ inputs.branch }} date: ${{ inputs.date }} arch: "amd64" - file_to_upload: "docs/cuopt/build/html/" - artifact-name: "cuopt_docs" container_image: "rapidsai/ci-conda:26.08-latest" script: "ci/build_docs.sh" wheel-build-cuopt-sh-client: diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index dbdf0993b5..7bb663277a 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -25,6 +25,7 @@ jobs: - conda-python-build - conda-python-tests - docs-build + - docs-preview - multi-gpu-cpp-tests - wheel-build-libcuopt - wheel-build-cuopt @@ -71,7 +72,9 @@ jobs: - '**' - '!.ai/**' - '!.clang-format' + - '!.claude-plugin/**' - '!.coderabbit.yaml' + - '!.cursor-plugin/**' - '!.gitattributes' - '!.github/.ai/**' - '!.github/.coderabbit_review_guide.md' @@ -104,6 +107,7 @@ jobs: - '!LICENSE' - '!README.md' - '!SECURITY.md' + - '!agents/**' - '!ci/build_wheel*.sh' - '!ci/check_style.sh' - '!ci/docker/**' @@ -116,9 +120,17 @@ jobs: - '!ci/test_self_hosted_service.sh' - '!ci/test_wheel*.sh' - '!ci/thirdparty-testing/**' + - '!ci/utils/sync_skills_version.sh' + - '!ci/utils/validate_skills.sh' + - '!conda/**' - '!container-builder/**' + - '!datasets/**' + - '!gemini-extension.json' - '!helmchart/**' + - '!img/**' - '!ngc/**' + - '!notebooks/**' + - '!skills/**' - '!sonar-project.properties' - '!sonarqube/**' - '!ucf/**' @@ -176,7 +188,7 @@ jobs: - '!ci/utils/sync_skills_version.sh' - '!ci/utils/validate_skills.sh' - '!container-builder/**' - - '!docs/**' + - '!fern/**' - '!gemini-extension.json' - '!helmchart/**' - '!img/**' @@ -244,7 +256,7 @@ jobs: - '!ci/utils/sync_skills_version.sh' - '!ci/utils/validate_skills.sh' - '!container-builder/**' - - '!docs/**' + - '!fern/**' - '!gemini-extension.json' - '!helmchart/**' - '!img/**' @@ -432,12 +444,38 @@ jobs: if: fromJSON(needs.changed-files.outputs.changed_file_groups).build_docs with: build_type: pull-request - node_type: "gpu-l4-latest-1" + node_type: "cpu4" arch: "amd64" - file_to_upload: "docs/cuopt/build/html/" - artifact-name: "cuopt_docs" container_image: "rapidsai/ci-conda:26.08-latest" script: "ci/build_docs.sh" + docs-preview: + needs: [docs-build, changed-files] + permissions: + contents: read + pull-requests: write + if: fromJSON(needs.changed-files.outputs.changed_file_groups).build_docs + runs-on: ubuntu-latest + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} + GH_TOKEN: ${{ github.token }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + - name: Install Fern CLI and publish preview + run: | + FERN_VERSION=$(jq -r .version fern/fern.config.json) + npm install -g "fern-api@${FERN_VERSION}" + OUTPUT=$(fern generate --docs --preview --id "$GITHUB_HEAD_REF" 2>&1) || true + echo "$OUTPUT" + URL=$(echo "$OUTPUT" | grep -oP 'Published docs to \K[^ ]+') + if [[ -n "$URL" ]]; then + PR_NUMBER="${GITHUB_REF_NAME##pull-request/}" + BODY=":herb: **Preview your docs:** ${URL}" + gh pr comment "${PR_NUMBER}" --body "${BODY}" --edit-last 2>/dev/null || \ + gh pr comment "${PR_NUMBER}" --body "${BODY}" + fi wheel-build-libcuopt: needs: [compute-matrix-filters, changed-files] if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_wheels diff --git a/.gitignore b/.gitignore index d4f98d1484..1be5d1a7ec 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ datasets/** ## Doxygen cpp/doxygen/html cpp/doxygen/xml +fern/.doxygen-xml/ # Jupyter Notebooks .ipynb_checkpoints @@ -80,3 +81,4 @@ cpp/include/cuopt/semantic_version.hpp # conda env (recommended name) .cuopt_env +cpp/build_split/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d084d6a64e..7a25ebbad9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,6 +41,7 @@ repos: cpp| conda| docs| + fern| notebooks ) - repo: https://github.com/pre-commit/mirrors-clang-format @@ -100,24 +101,29 @@ repos: ^docs/cuopt/source/cuopt-server/nim-operator/guide/| (^|/)VERSION$| (^|/)RAPIDS_BRANCH$| - [.](md|rst|avro|parquet|png|orc|gz|pkl|sas7bdat|msgpack|pickle|jpg|bz2|zlib)$| - ^docs/cuopt/source/cuopt-python/routing/routing-example[.]ipynb$| - ^docs/cuopt/source/versions1[.]json$| + [.](md|mdx|rst|avro|parquet|png|orc|gz|pkl|sas7bdat|msgpack|pickle|jpg|bz2|zlib)$| + ^helmchart/cuopt-server/(Chart[.]yaml|values[.]yaml)$| ^[.]cursor-plugin/plugin[.]json$| ^[.]claude-plugin/marketplace[.]json$| - ^gemini-extension[.]json$ + ^gemini-extension[.]json$| + ^fern/docs[.]yml$| + ^fern/docs-v[0-9].*[.]yml$| + ^fern/openapi/cuopt_spec[.]yaml$| + ^fern/docs/scripts/cuopt-install-version[.]js$| + ^fern/docs/pages/cuopt-server/nim-operator/guide/ - repo: https://github.com/zizmorcore/zizmor-pre-commit rev: v1.24.1 hooks: - id: zizmor - repo: local hooks: - - id: update-versions - name: Update versions1.json - entry: python ci/utils/update_doc_versions.py + - id: update-fern-install-version + name: Update Fern install version script + entry: python ci/utils/update_fern_install_version.py language: system - files: docs/cuopt/source/versions1.json + pass_filenames: false + files: ^VERSION$ - id: sync-skills-version name: Sync skills version from VERSION entry: ci/utils/sync_skills_version.sh diff --git a/AGENTS.md b/AGENTS.md index 2c7f6e0c96..46d0e8eadc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ AI agent skills for NVIDIA cuOpt optimization engine. Skills live in **`skills/` ### Rules - `skills/cuopt-user-rules/` — Base rules for end users calling cuOpt (routing, LP, MILP, QP, install, server). Not for cuOpt internals — see `skills/cuopt-developer/`. Read first for user-facing tasks; choose skills from the index below by task and interface. -- `skills/cuopt-developer/` — Modify, build, test, debug, and contribute to cuOpt internals (C++/CUDA, Python, server, CI). Use for solver internals, PRs, DCO, and code conventions. For **VRP dimension** work (combine invariants, fwd/bwd propagation, new constraints/objectives in the routing engine), read **`skills/cuopt-developer/references/vrp_skills.md`** in addition to `SKILL.md`. +- `skills/cuopt-developer/` — Modify, build, test, debug, and contribute to cuOpt internals (C++/CUDA, Python, server, CI). Use for solver internals, PRs, DCO, and code conventions. **Also read for any CI or workflow change** (new jobs, new scripts, dependency additions) — the existing pipeline in `ci/`, `pr.yaml`, and `build.yaml` must be understood before adding anything new. **Before writing a custom script to extract, parse, or transform content, check whether an established tool already exists** (e.g. numpydoc for Python docstrings, Doxygen for C/C++ headers, pandoc for markup conversion) — custom parsers are a last resort. For **VRP dimension** work (combine invariants, fwd/bwd propagation, new constraints/objectives in the routing engine), read **`skills/cuopt-developer/references/vrp_skills.md`** in addition to `SKILL.md`. - `skills/cuopt-skill-evolution/` — Skill evolution: after solving a non-trivial problem, propose skill updates to capture generalizable learnings. ### Common (concepts only; no API code) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 841e114a34..bd1fe350e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -241,13 +241,9 @@ pytest -v ${CUOPT_HOME}/python/cuopt/cuopt/tests ## gRPC Remote Execution NVIDIA cuOpt includes a gRPC-based remote execution system for running solves on a -GPU server from a program using the API locally. User documentation lives under `docs/cuopt/source/cuopt-grpc/` (Sphinx **gRPC remote execution** section): - -- `quick-start.rst` — Install/Docker/selector, how remote execution works, minimal LP and CLI examples (default C bundle). -- `advanced.rst` — TLS, tuning, limitations, troubleshooting. -- `examples.rst`, `api.rst` — Sample patterns and RPC overview. -- `docs/cuopt/source/cuopt-grpc/grpc-server-architecture.md` — Short **gRPC server behavior** page in user docs. -- `cpp/docs/grpc-server-architecture.md` — Full contributor reference (IPC, C++ source map, streaming). +GPU server from a program using the API locally. User documentation for gRPC lives in +`fern/docs/pages/cuopt-grpc/`. The contributor reference for the gRPC server internals +is at `cpp/docs/grpc-server-architecture.md`. ## Debugging cuOpt diff --git a/README.md b/README.md index 1a8c9c4fee..df4101ff00 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ For the latest version, ensure you are on the `main` branch. [cuOpt Documentation](https://docs.nvidia.com/cuopt/user-guide/latest/introduction.html) +## Contributing to the Documentation + +See [`fern/README.md`](fern/README.md) for how to edit pages, add API symbols or examples, and run a local preview. + ## Supported APIs cuOpt supports the following APIs: diff --git a/build.sh b/build.sh index 8293818dfa..883108faa4 100755 --- a/build.sh +++ b/build.sh @@ -14,7 +14,7 @@ ARGS=$* REPODIR=$(cd "$(dirname "$0")"; pwd) LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build} -VALIDARGS="clean codegen libcuopt cuopt_grpc_server cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" +VALIDARGS="clean codegen libcuopt cuopt_grpc_server cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn --publish-docs --preview -h --help" HELP="$0 [ ...] [ ...] where is: clean - remove all existing build artifacts and configuration (start over) @@ -24,7 +24,10 @@ HELP="$0 [ ...] [ ...] cuopt - build the cuopt Python package cuopt_server - build the cuopt_server Python package cuopt_sh_client - build cuopt self host client - docs - build the docs + docs - build the Fern docs (generate MDX + validate); starts local + preview server at http://localhost:3000 when done. + Pass --publish-docs to publish to production, or + --preview for a CI PR preview build deb - build deb package (requires libcuopt to be built first) and is: -v - verbose build mode @@ -63,8 +66,7 @@ PY_LIBCUOPT_BUILD_DIR=${REPODIR}/python/libcuopt/build CUOPT_BUILD_DIR=${REPODIR}/python/cuopt/build CUOPT_SERVER_BUILD_DIR=${REPODIR}/python/cuopt_server/build CUOPT_SH_CLIENT_BUILD_DIR=${REPODIR}/python/cuopt_self_hosted/build -DOCS_BUILD_DIR=${REPODIR}/docs/cuopt/build -BUILD_DIRS="${LIBCUOPT_BUILD_DIR} ${CUOPT_BUILD_DIR} ${CUOPT_SERVER_BUILD_DIR} ${CUOPT_SERVICE_CLIENT_BUILD_DIR} ${CUOPT_SH_CLIENT_BUILD_DIR} ${PY_LIBCUOPT_BUILD_DIR} ${DOCS_BUILD_DIR}" +BUILD_DIRS="${LIBCUOPT_BUILD_DIR} ${CUOPT_BUILD_DIR} ${CUOPT_SERVER_BUILD_DIR} ${CUOPT_SERVICE_CLIENT_BUILD_DIR} ${CUOPT_SH_CLIENT_BUILD_DIR} ${PY_LIBCUOPT_BUILD_DIR}" # Set defaults for vars modified by flags to this script VERBOSE_FLAG="" @@ -432,12 +434,10 @@ if buildAll || hasArg cuopt_sh_client; then python "${PYTHON_ARGS_FOR_INSTALL[@]}" . fi -# Build the docs (opt-in; pass 'docs' explicitly to build) +# Build the Fern docs (opt-in; pass 'docs' explicitly to build) if hasArg docs; then - cd "${REPODIR}"/cpp/doxygen - doxygen Doxyfile - - cd "${REPODIR}"/docs/cuopt - make clean - make html linkcheck + DOCS_FLAG="" + hasArg --publish-docs && DOCS_FLAG="--publish-docs" + hasArg --preview && DOCS_FLAG="--preview" + bash "${REPODIR}/fern/build_docs.sh" ${DOCS_FLAG} fi diff --git a/ci/build_docs.sh b/ci/build_docs.sh index c9fc8a5fe8..b94012112f 100755 --- a/ci/build_docs.sh +++ b/ci/build_docs.sh @@ -5,7 +5,7 @@ set -euo pipefail -rapids-logger "Create test conda environment" +rapids-logger "Create docs conda environment" . /opt/conda/etc/profile.d/conda.sh rapids-logger "Configuring conda strict channel priority" @@ -21,7 +21,6 @@ CPP_CHANNEL=$(rapids-download-from-github "$(rapids-artifact-name conda_cpp libc PYTHON_CHANNEL=$(rapids-download-from-github "$(rapids-artifact-name conda_python cuopt cuopt --py "$RAPIDS_PY_VERSION" --cuda "$RAPIDS_CUDA_VERSION")") rapids-logger "Generating conda environment YAML" - rapids-dependency-file-generator \ --output conda \ --file-key docs \ @@ -34,5 +33,10 @@ conda activate docs rapids-print-env -rapids-logger "Build Docs" -./build.sh docs +if [[ "${RAPIDS_BUILD_TYPE:-branch}" == "pull-request" ]]; then + rapids-logger "Building and publishing Fern PR preview" + ./build.sh docs --preview +else + rapids-logger "Building and publishing Fern docs to production" + ./build.sh docs --publish-docs +fi diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index 0e776c6043..b3b0ea2e11 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -117,11 +117,9 @@ for DEP in "${DEPENDENCIES[@]}"; do for FILE in python/*/pyproject.toml; do sed_runner "/\"${DEP}==/ s/==.*\"/==${NEXT_SHORT_TAG_PEP440}.*,>=0.0.0a0\"/g" "${FILE}" done - for FILE in docs/cuopt/source/*/quick-start.rst README.md; do - sed_runner "/${DEP}\(-cu[[:digit:]]\{2\}\)\{0,1\}==/ s/==[0-9]\+\.[0-9]\+\.\*/==${NEXT_SHORT_TAG_PEP440}.\*/g" "${FILE}" - sed_runner "/${DEP}=/ s/=[0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?[^ ]*/=${NEXT_SHORT_TAG}.*/g" "${FILE}" - sed_runner "/${DEP}:/ s/:[0-9]\{2\}\.[0-9]\{1,2\}\.[0-9]\+\(-cuda[0-9]\+\.[0-9]\+-\)\(py[0-9]\+\)/:${DOCKER_TAG}\1\2/g" "${FILE}" - done + sed_runner "/${DEP}\(-cu[[:digit:]]\{2\}\)\{0,1\}==/ s/==[0-9]\+\.[0-9]\+\.\*/==${NEXT_SHORT_TAG_PEP440}.\*/g" README.md + sed_runner "/${DEP}=/ s/=[0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?[^ ]*/=${NEXT_SHORT_TAG}.*/g" README.md + sed_runner "/${DEP}:/ s/:[0-9]\{2\}\.[0-9]\{1,2\}\.[0-9]\+\(-cuda[0-9]\+\.[0-9]\+-\)\(py[0-9]\+\)/:${DOCKER_TAG}\1\2/g" README.md done # Update README.md version badge @@ -135,6 +133,10 @@ sed_runner 's/\(tag: "\)[0-9][0-9]\.[0-9]\+\.[0-9]\+\(-cuda12\.9-py3\.12"\)/\1'$ sed_runner 's/\(appVersion: \)[0-9][0-9]\.[0-9]\+\.[0-9]\+/\1'${DOCKER_TAG}'/g' helmchart/cuopt-server/Chart.yaml sed_runner 's/\(version: \)[0-9][0-9]\.[0-9]\+\.[0-9]\+/\1'${DOCKER_TAG}'/g' helmchart/cuopt-server/Chart.yaml +# Update Fern docs version switcher and install widget +python ci/utils/update_fern_versions.py +python ci/utils/update_fern_install_version.py + # CI files - context-aware branch references and version updates for FILE in .github/workflows/*.yaml; do sed_runner "/shared-workflows/ s|@.*|@${RAPIDS_BRANCH_NAME}|g" "${FILE}" @@ -142,15 +144,3 @@ for FILE in .github/workflows/*.yaml; do # Scoped to rapidsai/ so unrelated images like python:3.14-slim aren't rewritten. sed_runner "/rapidsai\// s|:[0-9]*\\.[0-9]*-|:${NEXT_SHORT_TAG}-|g" "${FILE}" done - -# Documentation references - context-aware -if [[ "${RUN_CONTEXT}" == "main" ]]; then - # In main context, keep external documentation on main (no changes needed) - : -elif [[ "${RUN_CONTEXT}" == "release" ]]; then - # In release context, use release branch for external documentation links (word boundaries to avoid partial matches) - sed_runner "s|\\bmain\\b|release/${NEXT_SHORT_TAG}|g" docs/cuopt/source/faq.rst -fi - -# Update docs version switcher to include the new version -python ci/utils/update_doc_versions.py diff --git a/ci/test_doc_examples.sh b/ci/test_doc_examples.sh index 7d45f0dc78..d6034b51ba 100755 --- a/ci/test_doc_examples.sh +++ b/ci/test_doc_examples.sh @@ -25,7 +25,7 @@ SKIPPED_TESTS=0 # Directory setup SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" -DOCS_ROOT="${PROJECT_ROOT}/docs/cuopt/source" +DOCS_ROOT="${PROJECT_ROOT}/fern/docs/pages" RESULTS_DIR="${PROJECT_ROOT}/test-results" SERVER_PID="" diff --git a/ci/test_wheel_cuopt_server.sh b/ci/test_wheel_cuopt_server.sh index df1749a3b5..ddf9c0e5c1 100755 --- a/ci/test_wheel_cuopt_server.sh +++ b/ci/test_wheel_cuopt_server.sh @@ -50,7 +50,7 @@ timeout 30m ./ci/run_cuopt_server_pytests.sh \ --junitxml="${RAPIDS_TESTS_DIR}/junit-wheel-cuopt-server.xml" \ --verbose --capture=no || FAILED_STEPS+=("pytest cuopt-server (wheel)") -# Run documentation tests +# Run documentation examples ./ci/test_doc_examples.sh || FAILED_STEPS+=("doc examples") # Generate nightly test report diff --git a/ci/utils/update_doc_versions.py b/ci/utils/update_doc_versions.py deleted file mode 100644 index 8c8c29b6b3..0000000000 --- a/ci/utils/update_doc_versions.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 - -# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -""" -Script to update versions1.json with new version from VERSION file. -This script reads the current version from the VERSION file and adds it to versions1.json -if it doesn't already exist, setting it as the latest and preferred version. -""" - -import json -from pathlib import Path - - -def update_versions(version_file_path, versions_file_path): - """Update the versions list with the new version.""" - # Read VERSION file - with open(version_file_path, "r") as f: - version = f.read().strip() - if not version: - raise ValueError("VERSION file is empty") - - # Read versions1.json file - with open(versions_file_path, "r") as f: - versions = json.load(f) - - # Check if version already exists - for version_entry in versions: - if version_entry.get("version") == version: - print(f"Version {version} already exists in versions1.json") - return False - - # Remove "latest" and "preferred" from existing entries - for version_entry in versions: - if version_entry.get("name") == "latest": - version_entry.pop("name", None) - if version_entry.get("preferred"): - version_entry.pop("preferred", None) - - # Create new entry for the current version - new_entry = { - "version": version, - "url": f"https://docs.nvidia.com/cuopt/user-guide/{version}/", - "name": "latest", - "preferred": True, - } - - # Add new entry at the beginning (most recent first) - versions.insert(0, new_entry) - - # Write updated versions back to file - with open(versions_file_path, "w") as f: - json.dump(versions, f, indent=2) - f.write("\n") - - return True - - -def main(): - """Main function to update versions1.json.""" - # Get the repository root directory (assuming script is run from repo root) - repo_root = Path.cwd() - - # Hard-coded file paths - version_file_path = repo_root / "VERSION" - versions_file_path = ( - repo_root / "docs" / "cuopt" / "source" / "versions1.json" - ) - - # Update versions - update_versions(version_file_path, versions_file_path) - - -if __name__ == "__main__": - main() diff --git a/ci/utils/update_fern_install_version.py b/ci/utils/update_fern_install_version.py new file mode 100644 index 0000000000..134989427f --- /dev/null +++ b/ci/utils/update_fern_install_version.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Regenerate fern/docs/scripts/cuopt-install-version.js from the VERSION file. + +Runs as a pre-commit hook whenever VERSION changes, keeping the Fern install +selector widget in sync without requiring cuopt_server to be importable. +""" + +from pathlib import Path + + +def main(): + repo_root = Path(__file__).parent.parent.parent + version = (repo_root / "VERSION").read_text().strip() + if not version: + raise ValueError("VERSION file is empty") + + parts = version.split(".") + major, minor = int(parts[0]), int(parts[1]) if len(parts) > 1 else 0 + pip_ver = f"{major}.{minor}" + conda_ver = f"{major:02d}.{minor:02d}" + + out = repo_root / "fern/docs/scripts/cuopt-install-version.js" + out.write_text( + f'window.CUOPT_INSTALL_VERSION = {{"conda": "{conda_ver}", "pip": "{pip_ver}"}};\n' + ) + print( + f"Updated {out.relative_to(repo_root)} (conda={conda_ver}, pip={pip_ver})" + ) + + +if __name__ == "__main__": + main() diff --git a/ci/utils/update_fern_versions.py b/ci/utils/update_fern_versions.py new file mode 100644 index 0000000000..1e7b3194d4 --- /dev/null +++ b/ci/utils/update_fern_versions.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Add a new version entry to fern/docs.yml when cutting a release. + +Called by ci/release/update-version.sh. Inserts the new version at the top of the +versions list and demotes the previous entry (removes "(Latest)" suffix). If the +version already exists, exits cleanly without modifying the file. + +The script uses line-based editing to preserve the existing YAML formatting and +the rapids-pre-commit-hooks suppression comment on the latest entry. +""" + +import re +from pathlib import Path + + +def main(): + repo_root = Path(__file__).parent.parent.parent + version = (repo_root / "VERSION").read_text().strip() + if not version: + raise ValueError("VERSION file is empty") + + # rapids-pre-commit-hooks: disable-next-line + # Derive short tag (e.g. "26.08.00" → "26.08") and docs-yml path suffix + parts = version.split(".") + major, minor = int(parts[0]), int(parts[1]) if len(parts) > 1 else 0 + short = f"{major:02d}.{minor:02d}" + display = f"{short} (Latest)" + path_val = f"docs-v{short}.yml" + + docs_yml = repo_root / "fern/docs.yml" + text = docs_yml.read_text() + + # Bail out if this version already exists + if f'display-name: "{short}' in text: + print(f"Version {short} already present in fern/docs.yml") + return + + # Demote existing "(Latest)" entry: strip the suffix and remove the suppress comment + text = re.sub( + r" # rapids-pre-commit-hooks: disable-next-line\n" + r' - display-name: "([^"]+) \(Latest\)"', + r' - display-name: "\1"', + text, + ) + + # Insert new entry after the `versions:` key + new_entry = ( + f" # rapids-pre-commit-hooks: disable-next-line\n" + f' - display-name: "{display}"\n' + f" path: {path_val}\n" + ) + text = re.sub(r"(versions:\n)", r"\1" + new_entry, text, count=1) + + docs_yml.write_text(text) + print(f"Added {display} to fern/docs.yml") + + +if __name__ == "__main__": + main() diff --git a/conda/environments/all_cuda-129_arch-aarch64.yaml b/conda/environments/all_cuda-129_arch-aarch64.yaml index 6975b7a552..4942f61d3a 100644 --- a/conda/environments/all_cuda-129_arch-aarch64.yaml +++ b/conda/environments/all_cuda-129_arch-aarch64.yaml @@ -5,7 +5,6 @@ channels: - rapidsai - conda-forge dependencies: -- breathe - bzip2 - c-ares - c-compiler @@ -26,6 +25,7 @@ dependencies: - fastapi - gcc_linux-aarch64=14.* - ipython +- jq - jsonref==1.1.0 - libabseil - libboost-devel @@ -38,11 +38,10 @@ dependencies: - libprotobuf - libraft-headers==26.8.*,>=0.0.0a0 - librmm==26.8.*,>=0.0.0a0 -- make - msgpack-numpy==0.4.8 - msgpack-python==1.2.1 -- myst-parser - ninja +- nodejs - notebook - numba-cuda>=0.22.1 - numba>=0.60.0,<0.65.0 @@ -54,8 +53,8 @@ dependencies: - pip - pre-commit - psutil>=6.0.0 +- pydantic - pylibraft==26.8.*,>=0.0.0a0 -- pyrsistent - pytest-cov - pytest-rerunfailures - pytest-xdist @@ -70,19 +69,10 @@ dependencies: - ruamel.yaml>=0.18 - scikit-build-core>=0.11.0 - scipy>=1.14.1 -- sphinx -- sphinx-copybutton -- sphinx-design -- sphinx-markdown-tables -- sphinx_rtd_theme -- sphinxcontrib-openapi -- sphinxcontrib-websupport - sysroot_linux-aarch64==2.28 - tbb-devel - uvicorn==0.34.* - zlib - pip: - - nvidia-sphinx-theme - - swagger-plugin-for-sphinx - veroviz name: all_cuda-129_arch-aarch64 diff --git a/conda/environments/all_cuda-129_arch-x86_64.yaml b/conda/environments/all_cuda-129_arch-x86_64.yaml index 2785e5e66c..a1be42302a 100644 --- a/conda/environments/all_cuda-129_arch-x86_64.yaml +++ b/conda/environments/all_cuda-129_arch-x86_64.yaml @@ -5,7 +5,6 @@ channels: - rapidsai - conda-forge dependencies: -- breathe - bzip2 - c-ares - c-compiler @@ -26,6 +25,7 @@ dependencies: - fastapi - gcc_linux-64=14.* - ipython +- jq - jsonref==1.1.0 - libabseil - libboost-devel @@ -38,11 +38,10 @@ dependencies: - libprotobuf - libraft-headers==26.8.*,>=0.0.0a0 - librmm==26.8.*,>=0.0.0a0 -- make - msgpack-numpy==0.4.8 - msgpack-python==1.2.1 -- myst-parser - ninja +- nodejs - notebook - numba-cuda>=0.22.1 - numba>=0.60.0,<0.65.0 @@ -54,8 +53,8 @@ dependencies: - pip - pre-commit - psutil>=6.0.0 +- pydantic - pylibraft==26.8.*,>=0.0.0a0 -- pyrsistent - pytest-cov - pytest-rerunfailures - pytest-xdist @@ -70,19 +69,10 @@ dependencies: - ruamel.yaml>=0.18 - scikit-build-core>=0.11.0 - scipy>=1.14.1 -- sphinx -- sphinx-copybutton -- sphinx-design -- sphinx-markdown-tables -- sphinx_rtd_theme -- sphinxcontrib-openapi -- sphinxcontrib-websupport - sysroot_linux-64==2.28 - tbb-devel - uvicorn==0.34.* - zlib - pip: - - nvidia-sphinx-theme - - swagger-plugin-for-sphinx - veroviz name: all_cuda-129_arch-x86_64 diff --git a/conda/environments/all_cuda-133_arch-aarch64.yaml b/conda/environments/all_cuda-133_arch-aarch64.yaml index 25ac9c4048..b27ac6c329 100644 --- a/conda/environments/all_cuda-133_arch-aarch64.yaml +++ b/conda/environments/all_cuda-133_arch-aarch64.yaml @@ -5,7 +5,6 @@ channels: - rapidsai - conda-forge dependencies: -- breathe - bzip2 - c-ares - c-compiler @@ -26,6 +25,7 @@ dependencies: - fastapi - gcc_linux-aarch64=14.* - ipython +- jq - jsonref==1.1.0 - libabseil - libboost-devel @@ -38,11 +38,10 @@ dependencies: - libprotobuf - libraft-headers==26.8.*,>=0.0.0a0 - librmm==26.8.*,>=0.0.0a0 -- make - msgpack-numpy==0.4.8 - msgpack-python==1.2.1 -- myst-parser - ninja +- nodejs - notebook - numba-cuda>=0.22.1 - numba>=0.60.0,<0.65.0 @@ -54,8 +53,8 @@ dependencies: - pip - pre-commit - psutil>=6.0.0 +- pydantic - pylibraft==26.8.*,>=0.0.0a0 -- pyrsistent - pytest-cov - pytest-rerunfailures - pytest-xdist @@ -70,19 +69,10 @@ dependencies: - ruamel.yaml>=0.18 - scikit-build-core>=0.11.0 - scipy>=1.14.1 -- sphinx -- sphinx-copybutton -- sphinx-design -- sphinx-markdown-tables -- sphinx_rtd_theme -- sphinxcontrib-openapi -- sphinxcontrib-websupport - sysroot_linux-aarch64==2.28 - tbb-devel - uvicorn==0.34.* - zlib - pip: - - nvidia-sphinx-theme - - swagger-plugin-for-sphinx - veroviz name: all_cuda-133_arch-aarch64 diff --git a/conda/environments/all_cuda-133_arch-x86_64.yaml b/conda/environments/all_cuda-133_arch-x86_64.yaml index ad24412add..1874df2131 100644 --- a/conda/environments/all_cuda-133_arch-x86_64.yaml +++ b/conda/environments/all_cuda-133_arch-x86_64.yaml @@ -5,7 +5,6 @@ channels: - rapidsai - conda-forge dependencies: -- breathe - bzip2 - c-ares - c-compiler @@ -26,6 +25,7 @@ dependencies: - fastapi - gcc_linux-64=14.* - ipython +- jq - jsonref==1.1.0 - libabseil - libboost-devel @@ -38,11 +38,10 @@ dependencies: - libprotobuf - libraft-headers==26.8.*,>=0.0.0a0 - librmm==26.8.*,>=0.0.0a0 -- make - msgpack-numpy==0.4.8 - msgpack-python==1.2.1 -- myst-parser - ninja +- nodejs - notebook - numba-cuda>=0.22.1 - numba>=0.60.0,<0.65.0 @@ -54,8 +53,8 @@ dependencies: - pip - pre-commit - psutil>=6.0.0 +- pydantic - pylibraft==26.8.*,>=0.0.0a0 -- pyrsistent - pytest-cov - pytest-rerunfailures - pytest-xdist @@ -70,19 +69,10 @@ dependencies: - ruamel.yaml>=0.18 - scikit-build-core>=0.11.0 - scipy>=1.14.1 -- sphinx -- sphinx-copybutton -- sphinx-design -- sphinx-markdown-tables -- sphinx_rtd_theme -- sphinxcontrib-openapi -- sphinxcontrib-websupport - sysroot_linux-64==2.28 - tbb-devel - uvicorn==0.34.* - zlib - pip: - - nvidia-sphinx-theme - - swagger-plugin-for-sphinx - veroviz name: all_cuda-133_arch-x86_64 diff --git a/cpp/src/io/experimental_mps_fast/fast_fp64_parser.hpp b/cpp/src/io/experimental_mps_fast/fast_fp64_parser.hpp index 4f99c1cab9..ea75b44e16 100644 --- a/cpp/src/io/experimental_mps_fast/fast_fp64_parser.hpp +++ b/cpp/src/io/experimental_mps_fast/fast_fp64_parser.hpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/cpp/src/io/experimental_mps_fast/fast_parse_primitives.hpp b/cpp/src/io/experimental_mps_fast/fast_parse_primitives.hpp index 3d1700e579..7a692c6473 100644 --- a/cpp/src/io/experimental_mps_fast/fast_parse_primitives.hpp +++ b/cpp/src/io/experimental_mps_fast/fast_parse_primitives.hpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/cpp/src/io/experimental_mps_fast/fast_parser.cpp b/cpp/src/io/experimental_mps_fast/fast_parser.cpp index 4b74943a1d..72c50347f5 100644 --- a/cpp/src/io/experimental_mps_fast/fast_parser.cpp +++ b/cpp/src/io/experimental_mps_fast/fast_parser.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 #include "fast_parser.hpp" diff --git a/cpp/src/io/experimental_mps_fast/fast_parser.hpp b/cpp/src/io/experimental_mps_fast/fast_parser.hpp index ea0ec6fd99..d15b15de24 100644 --- a/cpp/src/io/experimental_mps_fast/fast_parser.hpp +++ b/cpp/src/io/experimental_mps_fast/fast_parser.hpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/cpp/src/io/experimental_mps_fast/file_reader.cpp b/cpp/src/io/experimental_mps_fast/file_reader.cpp index 2d1fa083b6..b1752a60d6 100644 --- a/cpp/src/io/experimental_mps_fast/file_reader.cpp +++ b/cpp/src/io/experimental_mps_fast/file_reader.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 #include "file_reader.hpp" diff --git a/cpp/src/io/experimental_mps_fast/file_reader.hpp b/cpp/src/io/experimental_mps_fast/file_reader.hpp index c2df168ac6..b80c286d9c 100644 --- a/cpp/src/io/experimental_mps_fast/file_reader.hpp +++ b/cpp/src/io/experimental_mps_fast/file_reader.hpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 // Input layer for the fast MPS parser: turns on-disk bytes (plain or .lz4) into one diff --git a/cpp/src/io/experimental_mps_fast/lz4_file_reader.cpp b/cpp/src/io/experimental_mps_fast/lz4_file_reader.cpp index ed0e6d5404..06f335d368 100644 --- a/cpp/src/io/experimental_mps_fast/lz4_file_reader.cpp +++ b/cpp/src/io/experimental_mps_fast/lz4_file_reader.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 #include "file_reader.hpp" diff --git a/cpp/src/io/experimental_mps_fast/mmap_region.hpp b/cpp/src/io/experimental_mps_fast/mmap_region.hpp index 48f2c1e1d1..94ff77d963 100644 --- a/cpp/src/io/experimental_mps_fast/mmap_region.hpp +++ b/cpp/src/io/experimental_mps_fast/mmap_region.hpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/cpp/src/io/experimental_mps_fast/mps_section_scanner.cpp b/cpp/src/io/experimental_mps_fast/mps_section_scanner.cpp index 32cbf3a105..6bc96dc08c 100644 --- a/cpp/src/io/experimental_mps_fast/mps_section_scanner.cpp +++ b/cpp/src/io/experimental_mps_fast/mps_section_scanner.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 #include "mps_section_scanner.hpp" diff --git a/cpp/src/io/experimental_mps_fast/mps_section_scanner.hpp b/cpp/src/io/experimental_mps_fast/mps_section_scanner.hpp index 1c16196d68..cc6efccce0 100644 --- a/cpp/src/io/experimental_mps_fast/mps_section_scanner.hpp +++ b/cpp/src/io/experimental_mps_fast/mps_section_scanner.hpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/cpp/src/io/experimental_mps_fast/nvtx_ranges.hpp b/cpp/src/io/experimental_mps_fast/nvtx_ranges.hpp index d05d6d948d..b4bf827ee5 100644 --- a/cpp/src/io/experimental_mps_fast/nvtx_ranges.hpp +++ b/cpp/src/io/experimental_mps_fast/nvtx_ranges.hpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/cpp/src/utilities/perf_counters.hpp b/cpp/src/utilities/perf_counters.hpp index 3fd47c2437..6044741266 100644 --- a/cpp/src/utilities/perf_counters.hpp +++ b/cpp/src/utilities/perf_counters.hpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // reserved. SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/cpp/tests/linear_programming/experimental_mps_fast/fast_fp64_parser_test.cpp b/cpp/tests/linear_programming/experimental_mps_fast/fast_fp64_parser_test.cpp index 192a707aa0..d03ed5cdfd 100644 --- a/cpp/tests/linear_programming/experimental_mps_fast/fast_fp64_parser_test.cpp +++ b/cpp/tests/linear_programming/experimental_mps_fast/fast_fp64_parser_test.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 #include "fast_fp64_parser.hpp" diff --git a/cpp/tests/linear_programming/experimental_mps_fast/fast_parser_edge_test.cpp b/cpp/tests/linear_programming/experimental_mps_fast/fast_parser_edge_test.cpp index 69cc29a0d3..2e4715f41c 100644 --- a/cpp/tests/linear_programming/experimental_mps_fast/fast_parser_edge_test.cpp +++ b/cpp/tests/linear_programming/experimental_mps_fast/fast_parser_edge_test.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 #include "fast_parser.hpp" diff --git a/dependencies.yaml b/dependencies.yaml index 22a5752514..80c08094dc 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -73,14 +73,11 @@ files: docs: output: none includes: - - cuda - - cuda_version - - depends_on_libcuopt - - depends_on_cuopt - - depends_on_cuopt_server - - depends_on_cuopt_sh_client - - docs - - py_version + - cuda_version + - depends_on_libcuopt + - depends_on_cuopt + - docs + - py_version py_build_libcuopt: output: pyproject pyproject_dir: python/libcuopt @@ -786,32 +783,19 @@ dependencies: common: - output_types: [conda] packages: + - jq + - nodejs # provides npm; used to install fern-api CLI + - *pyyaml - *doxygen - - &ipython ipython - - *jsonref - - make - - *msgpack_python - - *msgpack_numpy - - myst-parser + - fastapi - numpydoc - - pyrsistent - - breathe - - sphinx - - sphinx-copybutton - - sphinx-markdown-tables - - sphinx_rtd_theme - - sphinxcontrib-openapi - - sphinxcontrib-websupport - - sphinx-design - - pip: - - swagger-plugin-for-sphinx - - nvidia-sphinx-theme + - pydantic notebooks: common: - output_types: [conda, requirements] packages: - - *ipython + - ipython - notebook - output_types: [conda] packages: diff --git a/docs/cuopt/.editorconfig b/docs/cuopt/.editorconfig deleted file mode 100644 index c69a96fa27..0000000000 --- a/docs/cuopt/.editorconfig +++ /dev/null @@ -1,7 +0,0 @@ -[*] -indent_style = space -indent_size = 4 -charset = utf-8 -trim_trailing_whitespace = true -max_line_length = 120 -insert_final_newline = true diff --git a/docs/cuopt/Makefile b/docs/cuopt/Makefile deleted file mode 100644 index 3593784607..0000000000 --- a/docs/cuopt/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -n -v -W --keep-going -SPHINXBUILD = sphinx-build -SPHINXPROJ = cuOpt -SOURCEDIR = source -BUILDDIR = build - -# Default target: build documentation and run link check -all: html linkcheck - -# Build HTML documentation -html: - @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -# Check all external links in the documentation -linkcheck: - @$(SPHINXBUILD) -M linkcheck "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -# Show help -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: all html linkcheck help clean Makefile - -clean: - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/cuopt/README.md b/docs/cuopt/README.md deleted file mode 100644 index a6570eb1a0..0000000000 --- a/docs/cuopt/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Building Documentation - -Documentation dependencies are installed while installing the Conda environment, please refer to the [Build and Test](../../CONTRIBUTING.md#building-with-a-conda-environment) for more details. Assuming you have set-up the Conda environment, you can build the documentation along with all the cuOpt libraries by running: - -```bash -./build.sh -``` - -In subsequent runs where there are no changes to the cuOpt libraries, documentation can be built by running: - -1. From the root directory: -```bash -./build.sh docs -``` - -2. From the `docs/cuopt` directory: -```bash -make clean;make html -``` - -Outputs to `build/html/index.html` - -## View docs web page by opening HTML in browser: - -```bash -python -m http.server --directory=build/html/ -``` -Then, navigate a web browser to the IP address or hostname of the host machine at port 8000: - -``` -http://:8000 -``` -Now you can check if your docs edits formatted correctly, and read well. diff --git a/docs/cuopt/make.bat b/docs/cuopt/make.bat deleted file mode 100644 index 32f7216f8f..0000000000 --- a/docs/cuopt/make.bat +++ /dev/null @@ -1,39 +0,0 @@ -REM SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. -REM SPDX-License-Identifier: Apache-2.0 - -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build -set SPHINXPROJ=cuOpt - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/docs/cuopt/source/_static/install-selector.js b/docs/cuopt/source/_static/install-selector.js deleted file mode 100644 index ceeff29e65..0000000000 --- a/docs/cuopt/source/_static/install-selector.js +++ /dev/null @@ -1,461 +0,0 @@ -/** - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - * - * cuOpt install selector - generates install commands from user choices. - * Stable version comes from window.CUOPT_INSTALL_VERSION (injected at build from cuopt.__version__). - * Next = stable + 2 on minor with year rollover (YY.MM format). Update COMMANDS structure when commands change. - */ -(function () { - "use strict"; - - var ver = typeof window !== "undefined" && window.CUOPT_INSTALL_VERSION; - if (!ver || !ver.conda || !ver.pip) { - var root = document.getElementById("cuopt-install-selector"); - if (root) { - root.innerHTML = - '
' + - '

Install selector error: Version was not injected. ' + - 'Build the documentation (e.g. make html) so that cuopt-install-version.js is generated from the package version.

' + - "
"; - } - return; - } - - var V_CONDA = ver.conda; - var V = ver.pip; - var parts = V_CONDA.split("."); - var major = parseInt(parts[0], 10); - var minor = parseInt(parts[1], 10) || 0; - var nextMinor = minor + 2; - var nextMajor = major; - if (nextMinor > 12) { - nextMajor = major + 1; - nextMinor = nextMinor - 12; - } - var V_CONDA_NEXT = nextMajor + "." + (nextMinor < 10 ? "0" : "") + nextMinor; - var V_NEXT = nextMajor + "." + nextMinor; - - /* Shared Docker image lines: same tags are typically published to Docker Hub and NGC */ - var CONTAINER_CUOPT_LIB = { - stable: { - cu12: { - default: "docker pull nvidia/cuopt:latest-cu12", - run: "docker run --gpus all -it --rm nvidia/cuopt:latest-cu12 /bin/bash", - }, - cu13: { - default: "docker pull nvidia/cuopt:latest-cu13", - run: "docker run --gpus all -it --rm nvidia/cuopt:latest-cu13 /bin/bash", - }, - "cu13-ubi10": { - default: "docker pull nvidia/cuopt:latest-cu13-ubi10", - run: "docker run --gpus all -it --rm nvidia/cuopt:latest-cu13-ubi10 /bin/bash", - }, - }, - nightly: { - cu12: { - default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu12", - run: "docker run --gpus all -it --rm nvidia/cuopt:" + V_NEXT + ".0a-cu12 /bin/bash", - }, - cu13: { - default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu13", - run: "docker run --gpus all -it --rm nvidia/cuopt:" + V_NEXT + ".0a-cu13 /bin/bash", - }, - "cu13-ubi10": { - default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu13-ubi10", - run: "docker run --gpus all -it --rm nvidia/cuopt:" + V_NEXT + ".0a-cu13-ubi10 /bin/bash", - }, - }, - }; - - var COMMANDS = { - python: { - pip: { - stable: { - cu12: - "pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==" + - V + - ".*'", - cu13: - "pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu13==" + - V + - ".*'", - }, - nightly: { - cu12: - "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'cuopt-cu12==" + - V_NEXT + - ".*'", - cu13: - "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'cuopt-cu13==" + - V_NEXT + - ".*'", - }, - }, - conda: { - stable: { - cu12: - "conda install -c rapidsai -c conda-forge -c nvidia cuopt=" + - V_CONDA + - ".* cuda-version=12.9", - cu13: - "conda install -c rapidsai -c conda-forge -c nvidia cuopt=" + - V_CONDA + - ".* cuda-version=13.0", - }, - nightly: { - cu12: - "conda install -c rapidsai-nightly -c conda-forge -c nvidia cuopt=" + - V_CONDA_NEXT + - ".* cuda-version=12.9", - cu13: - "conda install -c rapidsai-nightly -c conda-forge -c nvidia cuopt=" + - V_CONDA_NEXT + - ".* cuda-version=13.0", - }, - }, - container: CONTAINER_CUOPT_LIB, - }, - c: { - pip: { - stable: { - cu12: - "pip install --extra-index-url=https://pypi.nvidia.com 'libcuopt-cu12==" + - V + - ".*'", - cu13: - "pip install --extra-index-url=https://pypi.nvidia.com 'libcuopt-cu13==" + - V + - ".*'", - }, - nightly: { - cu12: - "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'libcuopt-cu12==" + - V_NEXT + - ".*'", - cu13: - "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'libcuopt-cu13==" + - V_NEXT + - ".*'", - }, - }, - conda: { - stable: { - cu12: - "conda install -c rapidsai -c conda-forge -c nvidia libcuopt=" + - V_CONDA + - ".* cuda-version=12.9", - cu13: - "conda install -c rapidsai -c conda-forge -c nvidia libcuopt=" + - V_CONDA + - ".* cuda-version=13.0", - }, - nightly: { - cu12: - "conda install -c rapidsai-nightly -c conda-forge -c nvidia libcuopt=" + - V_CONDA_NEXT + - ".* cuda-version=12.9", - cu13: - "conda install -c rapidsai-nightly -c conda-forge -c nvidia libcuopt=" + - V_CONDA_NEXT + - ".* cuda-version=13.0", - }, - }, - container: CONTAINER_CUOPT_LIB, - }, - server: { - pip: { - stable: { - cu12: - "pip install --extra-index-url=https://pypi.nvidia.com 'nvidia-cuda-runtime-cu12==12.9.*' 'cuopt-server-cu12==" + - V + - ".*' 'cuopt-sh-client==" + - V_CONDA + - ".*'", - cu13: - "pip install --extra-index-url=https://pypi.nvidia.com 'nvidia-cuda-runtime==13.0.*' 'cuopt-server-cu13==" + - V + - ".*' 'cuopt-sh-client==" + - V_CONDA + - ".*'", - }, - nightly: { - cu12: - "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'cuopt-server-cu12==" + - V_NEXT + - ".*' 'cuopt-sh-client==" + - V_CONDA_NEXT + - ".*'", - cu13: - "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'cuopt-server-cu13==" + - V_NEXT + - ".*' 'cuopt-sh-client==" + - V_CONDA_NEXT + - ".*'", - }, - }, - conda: { - stable: { - default: - "conda install -c rapidsai -c conda-forge -c nvidia cuopt-server=" + - V_CONDA + - ".* cuopt-sh-client=" + - V_CONDA + - ".*", - }, - nightly: { - default: - "conda install -c rapidsai-nightly -c conda-forge -c nvidia cuopt-server=" + - V_CONDA_NEXT + - ".* cuopt-sh-client=" + - V_CONDA_NEXT + - ".*", - }, - }, - container: { - stable: { - cu12: { - default: "docker pull nvidia/cuopt:latest-cu12", - run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:latest-cu12", - }, - cu13: { - default: "docker pull nvidia/cuopt:latest-cu13", - run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:latest-cu13", - }, - "cu13-ubi10": { - default: "docker pull nvidia/cuopt:latest-cu13-ubi10", - run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:latest-cu13-ubi10", - }, - }, - nightly: { - cu12: { - default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu12", - run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:" + V_NEXT + ".0a-cu12", - }, - cu13: { - default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu13", - run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:" + V_NEXT + ".0a-cu13", - }, - "cu13-ubi10": { - default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu13-ubi10", - run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:" + V_NEXT + ".0a-cu13-ubi10", - }, - }, - }, - }, - }; - - var SUPPORTED_METHODS = { - python: ["pip", "conda", "container"], - c: ["pip", "conda", "container"], - server: ["pip", "conda", "container"], - cli: ["pip", "conda", "container"], - }; - - function getSelectedValue(name) { - var el = document.querySelector('input[name="' + name + '"]:checked'); - 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); - } - - function getCommand() { - var iface = getSelectedValue("cuopt-iface"); - var method = getSelectedValue("cuopt-method"); - var release = getSelectedValue("cuopt-release"); - var cuda = getSelectedValue("cuopt-cuda"); - - /* CLI uses libcuopt (c) install; cuopt_cli is shipped with libcuopt. */ - if (iface === "cli") { - iface = "c"; - release = "stable"; - cuda = "cu12"; - } - - var data = COMMANDS[iface] && COMMANDS[iface][method]; - if (!data || !data[release]) return ""; - - var cmd = ""; - if (method === "container") { - var variant = getSelectedValue("cuopt-variant") || "ubuntu"; - var baseCuda = cuda || "cu12"; - var cudaKey = baseCuda + (variant === "ubi10" ? "-ubi10" : ""); - var fallbackKey = (variant === "ubi10") ? "cu13-ubi10" : "cu12"; - var c = data[release][cudaKey] || data[release][fallbackKey]; - var hubPull = c.default; - var tag = "latest-cu12"; - var tm = hubPull.match(/docker pull nvidia\/cuopt:(\S+)/); - if (tm) tag = tm[1]; - var registry = getSelectedValue("cuopt-registry") || "hub"; - var runLine = c.run; - if (registry === "ngc" && release === "nightly") { - cmd = - "# Nightly cuOpt container images are not published to NVIDIA NGC; use Docker Hub for nightly builds.\n" + - "# (Select \"Docker Hub\" above for the same commands without this note.)\n\n" + - "# Docker Hub (docker.io) — no registry login required for public pulls\n" + - hubPull + - "\n\n" + - "# Run the container:\n" + - runLine; - } else if (registry === "ngc") { - runLine = runLine.replace(/nvidia\/cuopt:/g, "nvcr.io/nvidia/cuopt/cuopt:"); - cmd = - "# NVIDIA NGC (nvcr.io) — authenticate once per session, then pull:\n" + - "docker login nvcr.io\n" + - "# Username: $oauthtoken\n" + - "# Password: \n\n" + - "docker pull nvcr.io/nvidia/cuopt/cuopt:" + - tag + - "\n\n" + - "# Run the container:\n" + - runLine; - } else { - cmd = - "# Docker Hub (docker.io) — no registry login required for public pulls\n" + - hubPull + - "\n\n" + - "# Run the container:\n" + - runLine; - } - } else { - var key = data[release].cu12 && data[release].cu13 ? cuda : "default"; - cmd = data[release][key] || data[release].cu12 || data[release].cu13 || data[release].default || ""; - } - return cmd; - } - - function updateOutput() { - var out = document.getElementById("cuopt-cmd-out"); - var copyBtn = document.getElementById("cuopt-copy-btn"); - var cmd = getCommand(); - out.value = cmd; - out.style.display = cmd ? "block" : "none"; - copyBtn.style.display = cmd ? "inline-flex" : "none"; - } - - function updateVisibility() { - var method = getSelectedValue("cuopt-method"); - var iface = getSelectedValue("cuopt-iface"); - var allowed = SUPPORTED_METHODS[iface] || []; - var methodInputs = document.querySelectorAll('input[name="cuopt-method"]'); - methodInputs.forEach(function (input) { - var enabled = allowed.indexOf(input.value) !== -1; - input.disabled = !enabled; - var label = input.closest("label"); - if (label) label.style.display = enabled ? "" : "none"; - }); - if (allowed.indexOf(method) === -1 && allowed.length) { - var fallback = document.querySelector('input[name="cuopt-method"][value="' + allowed[0] + '"]'); - if (fallback) { - fallback.checked = true; - method = allowed[0]; - } - } - var cudaRow = document.getElementById("cuopt-cuda-row"); - var releaseRow = document.getElementById("cuopt-release-row"); - var releaseVisible = iface !== "cli"; - var ifaceForVariants = iface === "cli" ? "c" : iface; - var showCuda = - releaseVisible && - (method === "pip" || method === "conda" || method === "container") && - hasCudaVariants(ifaceForVariants, method); - cudaRow.style.display = showCuda ? "table-row" : "none"; - releaseRow.style.display = releaseVisible ? "table-row" : "none"; - var variantRow = document.getElementById("cuopt-variant-row"); - if (variantRow) { - variantRow.style.display = method === "container" ? "table-row" : "none"; - } - var registryRow = document.getElementById("cuopt-registry-row"); - if (registryRow) { - registryRow.style.display = method === "container" ? "table-row" : "none"; - } - updateOutput(); - } - - function copyToClipboard() { - var out = document.getElementById("cuopt-cmd-out"); - if (!out.value) return; - out.select(); - out.setSelectionRange(0, 99999); - try { - document.execCommand("copy"); - var btn = document.getElementById("cuopt-copy-btn"); - var orig = btn.textContent; - btn.textContent = "Copied!"; - setTimeout(function () { - btn.textContent = orig; - }, 1500); - } catch (e) {} - } - - function render() { - var root = document.getElementById("cuopt-install-selector"); - if (!root) return; - - root.innerHTML = - '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - "
Interface' + - '' + - '' + - '' + - '' + - '
Method' + - '' + - '' + - '' + - '
Release' + - '' + - '' + - '
CUDA' + - '' + - '' + - '
" + - '
' + - '' + - '
' + - "
"; - - ["cuopt-iface", "cuopt-method", "cuopt-release", "cuopt-cuda", "cuopt-variant", "cuopt-registry"].forEach( - function (name) { - var inputs = document.querySelectorAll('input[name="' + name + '"]'); - inputs.forEach(function (input) { - input.addEventListener("change", updateVisibility); - }); - } - ); - document.getElementById("cuopt-copy-btn").addEventListener("click", copyToClipboard); - updateVisibility(); - - var defaultIface = root.getAttribute("data-default-iface"); - if (defaultIface && ["python", "c", "server", "cli"].indexOf(defaultIface) !== -1) { - var radio = document.querySelector('input[name="cuopt-iface"][value="' + defaultIface + '"]'); - if (radio) { - radio.checked = true; - updateVisibility(); - } - } - } - - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", render); - } else { - render(); - } -})(); diff --git a/docs/cuopt/source/_static/swagger-nvidia.css b/docs/cuopt/source/_static/swagger-nvidia.css deleted file mode 100644 index 9672a6530c..0000000000 --- a/docs/cuopt/source/_static/swagger-nvidia.css +++ /dev/null @@ -1,4 +0,0 @@ -.swagger-ui pre.version { - background-color: unset; - border: unset; -} diff --git a/docs/cuopt/source/conf.py b/docs/cuopt/source/conf.py deleted file mode 100644 index b4749d0167..0000000000 --- a/docs/cuopt/source/conf.py +++ /dev/null @@ -1,429 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - - -from cuopt_server.webserver import app as cuoptapp -from fastapi.openapi.utils import get_openapi -import datetime -import cuopt -import yaml -from packaging.version import Version -import subprocess -import sys -import os -import tempfile -import json -from sphinx.util.fileutil import copy_asset_file -from pathlib import Path -from docutils import nodes -from docutils.parsers.rst import Directive, directives - -# Run cuopt server help command and save output -subprocess.run( - ["python", "-m", "cuopt_server.cuopt_service", "--help"], - stdout=open("cuopt-server/server-api/server-cli-help.txt", "w"), -) -# Run cuopt_sh help command and save output -subprocess.run( - ["cuopt_sh", "--help"], - stdout=open("cuopt-server/client-api/sh-cli-help.txt", "w"), -) - -# Run cuopt_cli help command and save output -subprocess.run( - ["cuopt_cli", "--help"], stdout=open("cuopt-cli/cuopt-cli-help.txt", "w") -) - -with open("cuopt_spec.yaml", "w") as f: - yaml.dump( - get_openapi( - title=cuoptapp.title, - version=cuoptapp.version, - openapi_version=cuoptapp.openapi_version, - description=cuoptapp.description, - summary=cuoptapp.summary, - routes=cuoptapp.routes, - ), - f, - ) - -# -- General configuration ------------------------------------------------ - -sys.path.insert(0, os.path.abspath("cpp/")) - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "breathe", - "sphinx.domains.cpp", - "sphinx.domains.c", - "sphinx.ext.intersphinx", - "sphinx.ext.autodoc", - "sphinx.ext.autosummary", - "sphinx_copybutton", - "numpydoc", - "sphinx_design", - "sphinx_markdown_tables", - "sphinx.ext.doctest", - "myst_parser", - "sphinx.ext.autosectionlabel", - "swagger_plugin_for_sphinx", -] - -swagger = [ - { - "name": "cuOpt API", - "id": "cuopt-api", - "page": "cuopt-api", - "options": { - "url": "_static/cuopt_spec.yaml", - "deepLinking": "true", - "showExtensions": "true", - "defaultModelsExpandDepth": -1, - }, - }, -] - -# Add any files to exclude from the build -exclude_patterns = ["hidden"] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# The suffix(es) of source filenames. -source_suffix = { - ".rst": "restructuredtext", - ".md": "markdown", -} - -# The master toctree document. -master_doc = "index" - -# General information about the project. -project = "NVIDIA cuOpt" -copyright = f"2021-{datetime.datetime.today().year}, NVIDIA Corporation" -author = "NVIDIA Corporation" - -# -- Options for HTML output ---------------------------------------------- -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -CUOPT_VERSION = Version(cuopt.__version__) -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# - -# The short X.Y version. -version = f"{CUOPT_VERSION.major:02}.{CUOPT_VERSION.minor:02}" -# The full version. -release = f"{CUOPT_VERSION.major:02}.{CUOPT_VERSION.minor:02}.{CUOPT_VERSION.micro:02}" - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = "en" - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "nvidia_sphinx_theme" -html_title = f"{project} ({version})" - -autosectionlabel_prefix_document = True - -html_theme_options = { - "icon_links": [ - { - "name": "GitHub", - "url": "https://github.com/NVIDIA/cuopt", - "icon": "fa-brands fa-github", - "type": "fontawesome", - }, - ], - "collapse_navigation": False, - "navigation_depth": 6, - "switcher": { - "json_url": "../versions1.json", - "version_match": release, - }, - "extra_head": [ # Adding Adobe Analytics - """ - - """ - ], - "extra_footer": [ - """ - - """ - ], - "show_nav_level": 2, -} - - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] -html_css_files = ["swagger-nvidia.css", "install-selector.css"] -html_js_files = ["cuopt-install-version.js", "install-selector.js"] -html_extra_path = ["versions1.json"] - - -# -- Options for Breathe (Doxygen) ---------------------------------------- -breathe_projects = {"libcuopt": "../../../cpp/doxygen/xml"} -breathe_default_project = "libcuopt" - -# Configure Breathe to handle file types -breathe_domain_by_extension = {"hpp": "cpp", "h": "c", "c": "c"} -breathe_implementation_filename_extensions = [".cpp", ".cu"] - -# Configure Breathe to handle CUDA and template attributes -breathe_doxygen_aliases = { - "int32_t": "int32_t", - "int8_t": "int8_t", -} - -# -- Options for LaTeX output --------------------------------------------- -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ( - master_doc, - "cuopt.tex", - f"{project} Documentation", - "NVIDIA Corporation", - "manual", - ) -] - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [(master_doc, "cuopt", f"{project} Documentation", [author], 1)] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - "cuopt", - f"{project} Documentation", - author, - "cuopt", - "One line description of project.", - "Miscellaneous", - ) -] - -# Config numpydoc -numpydoc_show_inherited_class_members = { - # option_context inherits undocumented members from the parent class - "cuopt.option_context": False, - # VType and CType inherit from str, sense inherits from int - # Hide inherited methods to avoid autodoc signature errors - "cuopt.linear_programming.problem.VType": False, - "cuopt.linear_programming.problem.CType": False, - "cuopt.linear_programming.problem.sense": False, -} - -# Rely on toctrees generated from autosummary on each of the pages we define -# rather than the autosummaries on the numpydoc auto-generated class pages. -numpydoc_class_members_toctree = False -numpydoc_attributes_as_param_list = False - -autoclass_content = "class" - -autodoc_mock_imports = [ - "cuopt.distance_engine.waypoint_matrix_wrapper.WaypointMatrix", - "cuopt.routing.vehicle_routing_wrapper.DataModel", - "cuopt.routing.vehicle_routing_wrapper.SolverSettings", - "cuopt.routing.DataModel.add_order_precedence", -] - -nitpick_ignore = [ - ("py:class", "cuopt.routing.vehicle_routing_wrapper.SolverSettings"), - ("py:class", "cuopt.routing.vehicle_routing_wrapper.DataModel"), - ( - "py:class", - "cuopt.distance_engine.waypoint_matrix_wrapper.WaypointMatrix", - ), - ("py:class", "enum.Enum"), - ("py:class", "object)"), # malformed type annotation reference - ("py:obj", "cuopt.routing.DataModel.add_order_precedence"), - ("py:obj", "cuopt_sh_client.SolverMethod.denominator"), - ("py:obj", "cuopt_sh_client.SolverMethod.imag"), - ("py:obj", "cuopt_sh_client.SolverMethod.numerator"), - ("py:obj", "cuopt_sh_client.SolverMethod.real"), - ("py:obj", "cuopt_sh_client.SolverMethod.as_integer_ratio"), - ("py:obj", "cuopt_sh_client.SolverMethod.bit_count"), - ("py:obj", "cuopt_sh_client.SolverMethod.bit_length"), - ("py:obj", "cuopt_sh_client.SolverMethod.conjugate"), - ("py:obj", "cuopt_sh_client.SolverMethod.from_bytes"), - ("py:obj", "cuopt_sh_client.SolverMethod.is_integer"), - ("py:obj", "cuopt_sh_client.SolverMethod.to_bytes"), - ("py:obj", "cuopt_sh_client.PDLPSolverMode.as_integer_ratio"), - ("py:obj", "cuopt_sh_client.PDLPSolverMode.conjugate"), - ("py:obj", "cuopt_sh_client.PDLPSolverMode.from_bytes"), - ("py:obj", "cuopt_sh_client.PDLPSolverMode.to_bytes"), - ("py:obj", "cuopt_sh_client.PDLPSolverMode.is_integer"), - ("py:obj", "cuopt_sh_client.PDLPSolverMode.bit_count"), - ("py:obj", "cuopt_sh_client.PDLPSolverMode.bit_length"), - ( - "py:obj", - "cuopt.linear_programming.data_model.DataModel.set_data_model_view", - ), - ( - "py:obj", - "cuopt.linear_programming.solver_settings.SolverSettings.to_base_type", - ), - ("c:type", "size_t"), - ("c:identifier", "int32_t"), - ("c:identifier", "int8_t"), -] - -# Regex patterns to ignore inherited methods from str/int base classes -# in VType, CType, and sense enums from linear_programming module -nitpick_ignore_regex = [ - # VType inherits from str - ignore all str methods - (r"py:obj", r"cuopt\.linear_programming\.problem\.VType\.\w+"), - # CType inherits from str - ignore all str methods - (r"py:obj", r"cuopt\.linear_programming\.problem\.CType\.\w+"), - # sense inherits from int - ignore all int methods/attributes - (r"py:obj", r"cuopt\.linear_programming\.problem\.sense\.\w+"), -] - - -def skip_unwanted_inherited_members(app, what, name, obj, skip, options): - # Dynamically get all public methods/attributes from builtin types - int_inherited = {m for m in dir(int) if not m.startswith("_")} - str_inherited = {m for m in dir(str) if not m.startswith("_")} - # Other methods to skip - other_to_skip = { - "to_base_type", - } - inherited_to_skip = int_inherited | str_inherited | other_to_skip - if name in inherited_to_skip: - return True - return skip - - -def write_project_json(app, _builder): - json_data = { - "name": "cuopt", - "version": cuopt.__version__, - "url": "https://github.com/nvidia/cuopt", - "description": "NVIDIA cuOpt is an optimization engine", - } - with tempfile.TemporaryDirectory() as d: - path = Path(d) / "project.json" - with open(path, "w") as f: - json.dump(json_data, f) - copy_asset_file(path, app.outdir / "project.json") - - -intersphinx_mapping = { - "python": ("https://docs.python.org/3", None), -} - -# Search settings -html_search_language = "en" -html_search_options = {"type": "default"} -html_search = True - -# Link checker settings -linkcheck_retries = 3 -linkcheck_timeout = 30 -linkcheck_workers = 5 -linkcheck_rate_limit_timeout = 60 - -# GitHub and GitLab link checker exceptions -linkcheck_ignore = [ - # GitHub (Rate Limited) - r"https://github\.com/.*", - r"https://api\.github\.com/.*", - r"https://raw\.githubusercontent\.com/.*", - r"https://gist\.github\.com/.*", - # GitLab (Rate Limited) - r"https://gitlab\.com/.*", - r"https://api\.gitlab\.com/.*", - r"https://gitlab\.org/.*", - r"https://api\.gitlab\.org/.*", -] - - -class InstallSelector(Directive): - """Embed the install selector widget. Optional :default-iface: (python, c, server, cli).""" - - optional_arguments = 0 - option_spec = {"default-iface": directives.unchanged} - has_content = False - - def run(self): - default_iface = ( - (self.options.get("default-iface") or "").strip().lower() - ) - if default_iface not in ("python", "c", "server", "cli"): - default_iface = "" - data_attr = ( - ' data-default-iface="' + default_iface + '"' - if default_iface - else "" - ) - html = '
" - return [nodes.raw("", html, format="html")] - - -def write_install_version_js(app): - """Write install selector version from cuopt.__version__ to output _static.""" - outdir = getattr(app.builder, "outdir", None) or getattr( - app.config, "outdir", None - ) - if not outdir: - return - static_dir = os.path.join(outdir, "_static") - os.makedirs(static_dir, exist_ok=True) - conda_ver = f"{CUOPT_VERSION.major:02}.{CUOPT_VERSION.minor:02}" - pip_ver = f"{CUOPT_VERSION.major}.{CUOPT_VERSION.minor}" - path = os.path.join(static_dir, "cuopt-install-version.js") - with open(path, "w") as f: - f.write( - 'window.CUOPT_INSTALL_VERSION = { "conda": "%s", "pip": "%s" };\n' - % (conda_ver, pip_ver) - ) - - -def setup(app): - app.add_directive("install-selector", InstallSelector) - app.setup_extension("sphinx.ext.autodoc") - app.connect("autodoc-skip-member", skip_unwanted_inherited_members) - app.connect("write-started", write_project_json) - app.connect("builder-inited", write_install_version_js) diff --git a/docs/cuopt/source/convex-features.rst b/docs/cuopt/source/convex-features.rst deleted file mode 100644 index 82de2dbc78..0000000000 --- a/docs/cuopt/source/convex-features.rst +++ /dev/null @@ -1,328 +0,0 @@ -============================ -Convex Optimization Features -============================ - -Availability -------------- - -The convex optimization solvers for Linear Programming (LP), Quadratic Programming (QP), Quadratically Constrained Quadratic Programming (QCQP), and Second-Order Cone Programming (SOCP) can be accessed in the following ways: - -- **Third-Party Modeling Languages**: cuOpt's convex optimization solvers can be called directly from the following third-party modeling languages. This allows you to GPU-accelerate your existing optimization workflow in these modeling languages. - - Supported modeling languages: - - .. list-table:: - :header-rows: 1 - :widths: 30 15 15 15 - - * - Language - - LP - - QP - - QCQP/SOCP - * - AMPL - - ✓ - - - - - * - CVXPY - - ✓ - - ✓ - - ✓ - * - GAMS - - ✓ - - ✓ - - - * - JuMP - - ✓ - - ✓ - - - * - PuLP - - ✓ - - - - - * - Pyomo - - ✓ - - - - - -.. note:: - QCQP/SOCP support is currently in **beta**, and is only supported in CVXPY among modeling languages. We hope to add support for QCQP/SOCP in other modeling languages soon. - -- **C API**: A native C API that provides direct low-level access to cuOpt's convex optimization solvers, enabling integration into any application or system that can interface with C. - -- **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`. - -- **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. - -Variable Bounds ---------------- - -Lower and upper bounds can be applied to each variable. If no variable bounds are specified, the default bounds are ``[-inf,+inf]``. - -Constraints ------------ - -The constraint matrix is specified in `Compressed Sparse Row (CSR) format `_. - -There are two ways to specify constraints to the LP solver: - -1. Using row_type and right-hand side: - - Constraints can be specified in the form: - - A*x {<=, =, >=} b - - where A is the constraint matrix in CSR format, x is the variable vector, and b is the right-hand side vector. The relationship {<=, =, >=} is specified via the ``row_type`` parameter. - -2. Using constraint bounds: - - Alternatively, constraints can be specified as two-sided inequalities: - - lb <= A*x <= ub - - where lb and ub are vectors of lower and upper bounds respectively. This form allows specifying both bounds on a single constraint. - - -Quadratic Objectives ---------------------- - -cuOpt supports problems with quadratic objectives of the form: - -.. code-block:: text - - minimize x^T*Q*x + c^T*x - subject to A*x {<=, =, >=} b, - lb <= x <= ub, - -where H = (Q + Q^T)/2 is a symmetric positive semidefinite matrix. Note that the Q matrix need not be symmetric, and is specified without the 1/2 factor that may be used by other solvers. - -.. note:: Currently, barrier is the only method that supports quadratic objectives. - -See :ref:`simple-qp-example-python` for an example of how to create a problem with a quadratic objective using the Python Modeling API. -See :ref:`simple-qp-example-c` for an example of how to create a problem with a quadratic objective using the C API. - -Quadratic Constraints (Beta) --------------------------------------- - -.. note:: Support for quadratic constraints is currently in **beta**. - -cuOpt supports quadratic constraints of the form - -.. code-block:: text - - x^T Q x + d^T x {<=, >=} alpha - -and translates them internally into **second-order cone** constraints. Problems with quadratic constraints have the form: - -.. code-block:: text - - minimize c^T*x + x^T Q x - subject to A*x {<=, =, >=} b, - x^T Q_i x + d_i^T x {<=, >=} alpha_i, i = 1, ..., p, - lb <= x <= ub. - -Quadratic constraints are supplied via ``addConstraint`` in Python, via :c:func:`cuOptAddQuadraticConstraint` in C, and via ``QCMATRIX`` sections in MPS. - -cuOpt accepts several different types of quadratic constraints: - -**Convex quadratic constraints**: - -These are constraints of the form - -.. code-block:: text - - x^T Q x + d^T x <= alpha - -where ``H = (Q + Q^T)/2`` is a symmetric positive semidefinite matrix, and - -.. code-block:: text - - x^T Q x + d^T x >= alpha - -where ``H = (Q + Q^T)/2`` is a symmetric negative semidefinite matrix. Note when specifying a convex quadratic constraint, the Q matrix need not be symmetric. - -**Second-order cone constraints**: - -These constrains are of the form: - -.. code-block:: text - - || (x_1, ..., x_n) ||_2 <= x_0. - -Second-order cone constraints must be specified as a quadratic constraint with a bound on the x_0 variable: - -.. code-block:: text - - x_1^2 + ... + x_n^2 - x_0^2 <= 0, x_0 >= 0. - -**Rotated second-order cone constraints**: - -These constraints are of the form: - - - x_2^2 + x_3^2 + ... + x_n^2 <= 2 * x_0 * x_1, x_0 >= 0, x_1 >= 0. - -Rotated second-order cone constraints must be specified as a quadratic constraint with a bounds on the x_0 and x_1 variables: - -.. code-block:: text - - x_2^2 + x_3^2 + ... + x_n^2 - 2 * x_0 * x_1 <= 0, x_0 >= 0, x_1 >= 0. - -In the Python and C APIs, supply one cross coefficient per variable pair (e.g. ``-2 * x_0 * x_1`` -in the quadratic expression). cuOpt canonicalizes input to one stored COO entry per pair. - -.. code-block:: text - - Q[x_0, x_1] = -2 * d (stored canonical coefficient for the cross term) - -For example, a single API term ``-2 * x_0 * x_1`` is stored as ``-2`` and defines the cone -``||tail||^2 <= 2 * x_0 * x_1``. - -When any quadratic constraint is present, cuOpt automatically selects the barrier method and disables presolve optimizations that apply only to linear problems. - -.note:: - -- Only ``<=`` and ``>=`` sense is supported. Equality quadratic constraints are not supported. - -**Python example — second-order cone** ``||(x_1, x_2)||_2 <= x_0``: - -.. code-block:: python - - x0 = problem.addVariable(name="x0", lb=0) # cone head, must be >= 0 - x1 = problem.addVariable(name="x1") - x2 = problem.addVariable(name="x2") - - problem.addConstraint(x1*x1 + x2*x2 - x0*x0 <= 0, name="soc") - -**Python example — rotated cone** ``x_2^2 + x_3^2 <= 2 * x_0 * x_1``: - -.. code-block:: python - - - x0 = problem.addVariable(name="x3", lb=0) # cone head, must be >= 0 - x1 = problem.addVariable(name="x4", lb=0) # cone head, must be >= 0 - x2 = problem.addVariable(name="x1") - x3 = problem.addVariable(name="x2") - - problem.addConstraint(x1 + x2 >= 2) - # x2^2 + x3^2 <= 2*x0*x1 - problem.addConstraint( - x2*x2 + x3*x3 - 2*x0*x1 <= 0, name="rotated_soc" - ) - -**C API:** Use :c:func:`cuOptAddQuadraticConstraint` to add convex quadratic constraints or second-order and rotated second-order cone constraints expressed as quadratic inequalities. - -.. note:: Problems with quadratic constraints always use the barrier solver regardless of the ``CUOPT_METHOD`` setting. - -Warm Start ------------ - -A warm starts allow a user to provide an initial solution to help PDLP converge faster. The initial ``primal`` and ``dual`` solutions can be specified by the user. - -Alternatively, previously run solutions can be used to warm start a new solve to decrease solve time. :ref:`Examples ` are shared on the self-hosted page. - -PDLP Solver Mode ----------------- -Users can control how the solver will operate by specifying the PDLP solver mode. The mode choice can drastically impact how fast a specific problem will be solved. Users are encouraged to test different modes to see which one fits the best their problem. - - -Method ------- - -**Concurrent**: The default method for solving linear programs. When concurrent is selected, cuOpt runs three algorithms in parallel: PDLP on the GPU, barrier (interior-point) on the GPU, and dual simplex on the CPU. A solution is returned from the algorithm that finishes first. - -**PDLP**: Primal-Dual Hybrid Gradient for Linear Program is an algorithm for solving large-scale linear programming problems on the GPU. PDLP does not attempt any matrix factorizations during the course of the solve. Select this method if your LP is so large that factorization will not fit into memory. By default PDLP solves to low relative tolerance and the solutions it returns do not lie at a vertex of the feasible region. Enable crossover to obtain a highly accurate basic solution from a PDLP solution. - -.. note:: - PDLP solves to 1e-4 relative accuracy by default. - -**Barrier**: The barrier method (also known as interior-point method) solves linear and quadratic programs using a primal-dual predictor-corrector algorithm. This method uses GPU-accelerated sparse Cholesky and sparse LDLT solves via cuDSS, and GPU-accelerated sparse matrix-vector and matrix-matrix operations via cuSparse. Barrier is particularly effective for large-scale problems and can automatically apply techniques like folding, dualization, and dense column elimination to improve performance. This method solves the linear systems at each iteration using the augmented system or the normal equations (ADAT). Enable crossover to obtain a highly accurate basic solution from a barrier solution. - -.. note:: - Barrier solves to 1e-8 relative accuracy by default. - -**Dual Simplex**: Dual simplex is the simplex method applied to the dual of the linear program. Dual simplex requires the basis factorization of linear program fit into memory. Select this method if your LP is small to medium sized, or if you require a high-quality basic solution. - -.. note:: - Dual Simplex solves to 1e-6 absolute accuracy by default. - - -Crossover ---------- - -Crossover allows you to obtain a high-quality basic solution from the results of a PDLP or barrier solve. When enabled, crossover converts the PDLP or barrier solution to a vertex solution (basic solution) with high accuracy. More details can be found :ref:`here `. - -.. note:: - Crossover is not supported for problems with quadratic objectives or quadratic constraints. - -Presolve --------- - -Presolve procedure is applied to the problem before the solver is called. It can be used to reduce the problem size and improve solve time. cuOpt supports presolve reductions using PSLP or Papilo for linear programming (LP) problems. For LP problems, PSLP presolve is always enabled by default. Users can manually select to disable presolve by setting this parameter to 0, enable Papilo presolve by setting this parameter to 1, or enable PSLP presolve by setting this parameter to 2. -Furthermore, for LP problems with Papilo presolver, when the dual solution is not needed, additional presolve procedures can be applied to further improve solve times. This is achieved by turning off dual postsolve with the ``CUOPT_DUAL_POSTSOLVE`` setting. - - -Logging -------- - -The ``CUOPT_LOG_FILE`` parameter can be set to write detailed solver logs for LP/QP/QCQP/SOCP problems. This parameter is available in all APIs that allow setting solver parameters except the cuOpt service. For the service, see the logging callback below. - -Logging Callback in the Service -------------------------------- - -In the cuOpt service API, the ``log_file`` value in ``solver_configs`` is ignored. - -If however you set the ``solver_logs`` flag on the ``/cuopt/request`` REST API call, users can fetch the log file content from the webserver at ``/cuopt/logs/{id}``. Using the logging callback feature through the cuOpt client is shown in :ref:`Examples ` on the self-hosted page. - - -Infeasibility Detection ------------------------ - -The PDLP solver includes the option to detect infeasible problems. If the infeasibilty detection is enabled in solver settings, PDLP will abort as soon as it concludes the problem is infeasible. - -.. note:: - Infeasibility detection is always enabled for dual simplex. - -Time Limit ----------- - -The user may specify a time limit to the solver. By default the solver runs until a solution is found or the problem is determined to be infeasible or unbounded. - -.. note:: - - Note that ``time_limit`` applies only to solve time inside the LP solver. This does not include time for network transfer, validation of input, and other operations that occur outside the solver. The overhead associated with these operations are usually small compared to the solve time. - - -.. _batch-mode: - -Batch Mode ----------- - -Users can submit a set of problems which will be solved in a batch. Problems will be solved at the same time in parallel to fully utilize the GPU. Checkout :ref:`self-hosted client ` example in thin client. - -.. warning:: Deprecated - - LP batch mode (Python ``cuopt.linear_programming.BatchSolve``, server requests - with a list of LP problems, and multi-file ``cuopt_sh`` LP submissions) is - deprecated and will be removed in a future release. Prefer sequential - ``cuopt.linear_programming.Solve`` calls, or implement your own parallelism - (for example with ``concurrent.futures``). Existing batch APIs still run in - parallel today; callers may see a ``DeprecationWarning`` or a deprecation - message in server ``warnings``. - -PDLP Precision Modes --------------------- - -By default, PDLP operates in the native precision of the problem type (FP64 for double-precision problems). The ``pdlp_precision`` parameter provides several modes: - -- **single**: Run PDLP internally in FP32, with automatic conversion of inputs and outputs. FP32 uses half the memory and allows PDHG iterations to be on average twice as fast, but may require more iterations to converge. Compatible with crossover (solution is converted back to FP64 before crossover) and concurrent mode (PDLP runs in FP32 while other solvers run in FP64). -- **mixed**: Use mixed precision SpMV during PDHG iterations. The constraint matrix is stored in FP32 while vectors and compute type remain in FP64, improving SpMV performance with limited impact on convergence. Convergence checking and restart logic always use the full FP64 matrix. -- **double**: Explicitly run in FP64 (same as default for double-precision problems). - -.. note:: The default precision is the native type of the problem (FP64 for double). - -Multi-GPU Mode --------------- - -Users can use multiple GPUs to solve a problem by specifying the ``num_gpus`` parameter. The feature is restricted to LP problems that uses concurrent mode and supports up to 2 GPUs at the moment. Using this mode will run PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources. diff --git a/docs/cuopt/source/convex-settings.rst b/docs/cuopt/source/convex-settings.rst deleted file mode 100644 index 4d7d8de669..0000000000 --- a/docs/cuopt/source/convex-settings.rst +++ /dev/null @@ -1,389 +0,0 @@ -================================================ -Convex Optimization Settings -================================================ - - -This page describes the parameter settings available for cuOpt's convex optimization solvers. These parameters are set as :ref:`parameter constants ` in case of C API and in case of Server Thin client as raw strings. Refer to examples in :doc:`C ` and :doc:`Server Thin client ` for more details. - -.. note:: - When setting parameters in thin client solver settings, remove ``CUOPT_`` from the parameter name and convert to lowercase. For example, ``CUOPT_TIME_LIMIT`` would be set as ``time_limit``. - -Common Parameters ------------------ - -We begin by describing parameters common to both the MILP and LP solvers - - -Time Limit -^^^^^^^^^^ -``CUOPT_TIME_LIMIT`` controls the time limit in seconds after which the solver will stop and return the current solution. -For performance reasons, cuOpt does not constantly checks for time limit. Thus, the solver -may run slightly over the limit. If set along with the iteration limit, cuOpt will stop when -the first limit (iteration or time) is hit. - - -.. note:: By default there is no time limit. So cuOpt will run until it finds an optimal solution, - or proves the problem is infeasible or unbounded. - - - -Log to Console -^^^^^^^^^^^^^^ -``CUOPT_LOG_TO_CONSOLE`` controls whether cuOpt should log information to the console during a solve. -If true, a logging info is written to the console, if false no logging info is written to the console (logs may still be written to a file.) - -.. note:: The default value is true. - -Log File -^^^^^^^^ -``CUOPT_LOG_FILE`` controls the name of a log file where cuOpt should write information about the solve. - -.. note:: The default value is ``""`` and no log file is written. This setting is ignored by the cuOpt service, use the log callback feature instead. - -Solution File -^^^^^^^^^^^^^ -``CUOPT_SOLUTION_FILE`` controls the name of a file where cuOpt should write the solution. - -.. note:: The default value is ``""`` and no solution file is written. This setting is ignored by the cuOpt service. - -User Problem File -^^^^^^^^^^^^^^^^^ -``CUOPT_USER_PROBLEM_FILE`` controls the name of a file where cuOpt should write the user problem. - -.. note:: The default value is ``""`` and no user problem file is written. This setting is ignored by the cuOpt service. - -Num CPU Threads -^^^^^^^^^^^^^^^ -``CUOPT_NUM_CPU_THREADS`` controls the number of CPU threads used in the LP and MIP solvers. Set this to a small value to limit -the amount of CPU resources cuOpt uses. Set this to a large value to improve solve times for CPU -parallel parts of the solvers. - -.. note:: By default the number of CPU threads is automatically determined based on the number of CPU cores. - -Presolve -^^^^^^^^ -``CUOPT_PRESOLVE`` controls which presolver to use for presolve reductions. -cuOpt provides presolve reductions for linear programming (LP) problems using either PSLP or Papilo, and for mixed-integer programming (MIP) problems using Papilo. By default, Papilo presolve is always enabled for MIP problems. For LP problems, PSLP presolve is always enabled by default. You can explicitly control the presolver by setting this parameter to 0 (disable presolve), 1 (Papilo), or 2 (PSLP). - -Dual Postsolve -^^^^^^^^^^^^^^ -``CUOPT_DUAL_POSTSOLVE`` controls whether dual postsolve is enabled when using Papilo presolver for LP problems. Disabling dual postsolve can improve solve time at the expense of not having -access to the dual solution. Enabled by default for LP when Papilo presolve is selected. This is not relevant for MIP problems. - -Linear Programming ------------------- - -We now describe the parameter settings used to control cuOpt's Linear Programming solvers - -Method -^^^^^^ - -``CUOPT_METHOD`` controls the method to solve the linear programming problem. Four methods are available: - -* ``Concurrent``: Use PDLP, dual simplex, and barrier in parallel (default). -* ``PDLP``: Use the PDLP method. -* ``Dual Simplex``: Use the dual simplex method. -* ``Barrier``: Use the barrier (interior-point) method. - -.. note:: The default method is ``Concurrent``. - -Default accuracy for each method: - -* PDLP solves to 1e-4 relative accuracy by default. -* Barrier solves to 1e-8 relative accuracy by default. -* Dual Simplex solves to 1e-6 *absolute* accuracy by default. - -C API users should use the constants defined in :ref:`method-constants` for this parameter. - -Server Thin client users should use the :class:`cuopt_sh_client.SolverMethod` for this parameter. - -PDLP Solver Mode -^^^^^^^^^^^^^^^^ - -``CUOPT_PDLP_SOLVER_MODE`` controls the mode under which PDLP should operate. The mode will change the way the -PDLP internally optimizes the problem. The mode choice can drastically impact how fast a -specific problem will be solved. Users are encouraged to test different modes to see which one -fits the best their problem. By default, the solver uses ``Stable3``, the best -overall mode from our experiments. For now, only three modes are available: ``Stable3``, -``Methodical1``, and ``Fast1``. - -For now, we do not offer a mechanism to know upfront which solver mode will be the best -for a specific problem. - -C API users should use the constants defined in :ref:`pdlp-solver-mode-constants` for this parameter. - -Server Thin client users should use the :class:`cuopt_sh_client.PDLPSolverMode` for this parameter. - -Iteration Limit -^^^^^^^^^^^^^^^ - -``CUOPT_ITERATION_LIMIT`` controls the iteration limit after which the solver will stop and return the current solution. -For performance reasons, cuOpt's does not constantly checks for iteration limit, thus, -the solver might run a few extra iterations over the limit. If set along with the time limit, -cuOpt will stop at the first limit (iteration or time) reached. - -.. note:: By default there is no iteration limit. So, cuOpt will run until it finds an optimal solution, - or proves the problem is infeasible or unbounded. - - -Number of GPUs -^^^^^^^^^^^^^^ - -``CUOPT_NUM_GPUS`` controls the number of GPUs to use for the solve. This setting is only relevant for LP problems that uses concurrent mode and supports up to 2 GPUs at the moment. Using this mode will run PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources. - - -Infeasibility Detection -^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_INFEASIBILITY_DETECTION`` controls whether PDLP should detect infeasibility. Note that infeasibility detection in PDLP -is not always accurate. Some problems detected as infeasible may converge under a different tolerance factor. -Detecting infeasibility consumes both more runtime and memory. The added runtime is between 3% and 7%, -the added memory consumption is between 10% and 20%. - -.. note:: By default PDLP will not detect infeasibility. Dual simplex will always detect infeasibility - regardless of this setting. - -Strict Infeasibility -^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_STRICT_INFEASIBILITY`` controls the strict infeasibility mode in PDLP. When true if either the current or the average solution -is detected as infeasible, PDLP will stop. When false both the current and average solution need to be -detected as infeasible for PDLP to stop. - -.. note:: The default value is false. - -.. _crossover: - -Crossover -^^^^^^^^^ - -``CUOPT_CROSSOVER`` controls whether PDLP or barrier should crossover to a basic solution after an optimal solution is found. -Changing this value has a significant impact on accuracy and runtime. -By default the solutions provided by PDLP and barrier do not lie at a vertex and thus may have many variables that lie -between their bounds. Enabling crossover allows the user to obtain a high-quality basic solution -that lies at a vertex of the feasible region. If n is the number of variables, and m is the number of -constraints, n - m variables will be on their bounds in a basic solution. - -.. note:: The default value is false. - -Save Best Primal So Far -^^^^^^^^^^^^^^^^^^^^^^^ -``CUOPT_SAVE_BEST_PRIMAL_SOLUTION`` controls whether PDLP should save the best primal solution so far -With this parameter set to true, PDLP -* Will always prioritize a primal feasible to a non primal feasible -* If a new primal feasible is found, the one with the best primal objective will be kept -* If no primal feasible was found, the one with the lowest primal residual will be kept -* If two have the same primal residual, the one with the best objective will be kept - -.. note:: The default value is false. - -First Primal Feasible -^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_FIRST_PRIMAL_FEASIBLE`` controls whether PDLP should stop when the first primal feasible solution is found. - -.. note:: The default value is false. - -Per Constraint Residual -^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_PER_CONSTRAINT_RESIDUAL`` controls whether PDLP should compute the primal & dual residual per constraint instead of globally. - -.. note:: The default value is false. - -PDLP Precision -^^^^^^^^^^^^^^ - -``CUOPT_PDLP_PRECISION`` controls the precision mode used by the PDLP solver. The following modes are -available: - -- **default** (-1): Use the native precision of the problem type (FP64 for double-precision problems). -- **single** (0): Run PDLP internally in FP32 (float). Inputs are converted from FP64 to FP32 before - solving and outputs are converted back to FP64. FP32 uses half the memory and allows PDHG iterations - to be on average twice as fast, but may require more iterations to converge due to reduced numerical - accuracy. Compatible with crossover (solution is converted back to FP64 before crossover runs) and - concurrent mode (the PDLP leg runs in FP32 while Dual Simplex and Barrier run in FP64). -- **double** (1): Explicitly run in FP64 (same as default for double-precision problems). -- **mixed** (2): Use mixed precision sparse matrix-vector products (SpMV) during PDHG iterations. The - constraint matrix and its transpose are stored in FP32 while vectors and the compute type remain in - FP64, improving SpMV performance. Convergence checking and restart logic always use the - full FP64 matrix, so this mode does not reduce overall memory usage. This provides a middle ground - between full FP64 and FP32: faster PDHG iterations with limited impact on convergence. - -.. note:: The default value is 0 (default precision). - -Barrier Solver Settings -^^^^^^^^^^^^^^^^^^^^^^^^ - -The following settings control the behavior of the barrier (interior-point) method: - -Folding -""""""" - -``CUOPT_FOLDING`` controls whether to fold the linear program. Folding can reduce problem size by exploiting symmetry in the problem. - -* ``-1``: Automatic (default) - cuOpt decides whether to fold based on problem characteristics -* ``0``: Disable folding -* ``1``: Force folding to run - -.. note:: The default value is ``-1`` (automatic). - -Dualize -""""""" - -``CUOPT_DUALIZE`` controls whether to dualize the linear program in presolve. Dualizing can improve solve time for problems, with inequality constraints, where there are more constraints than variables. - -* ``-1``: Automatic (default) - cuOpt decides whether to dualize based on problem characteristics -* ``0``: Don't attempt to dualize -* ``1``: Force dualize - -.. note:: The default value is ``-1`` (automatic). - -Ordering -"""""""" - -``CUOPT_ORDERING`` controls the ordering algorithm used by cuDSS for sparse factorizations. The ordering can significantly impact solver run time. - -* ``-1``: Automatic (default) - cuOpt selects the best ordering -* ``0``: cuDSS default ordering -* ``1``: AMD (Approximate Minimum Degree) ordering - -.. note:: The default value is ``-1`` (automatic). - -Augmented System -"""""""""""""""" - -``CUOPT_AUGMENTED`` controls which linear system to solve in the barrier method. - -* ``-1``: Automatic (default) - cuOpt selects the best linear system to solve -* ``0``: Solve the ADAT system (normal equations) -* ``1``: Solve the augmented system - -.. note:: The default value is ``-1`` (automatic). The augmented system may be more stable for some problems, while ADAT may be faster for others. - -Eliminate Dense Columns -"""""""""""""""""""""""" - -``CUOPT_ELIMINATE_DENSE_COLUMNS`` controls whether to eliminate dense columns from the constraint matrix before solving. Eliminating dense columns can improve performance by reducing fill-in during factorization. -However, extra solves must be performed at each iteration. - -* ``true``: Eliminate dense columns (default) -* ``false``: Don't eliminate dense columns - -This setting only has an effect when the ADAT (normal equation) system is solved. - -.. note:: The default value is ``true``. - -cuDSS Deterministic Mode -""""""""""""""""""""""""" - -``CUOPT_CUDSS_DETERMINISTIC`` controls whether cuDSS operates in deterministic mode. Deterministic mode ensures reproducible results across runs but may be slower. - -* ``true``: Use deterministic mode -* ``false``: Use non-deterministic mode (default) - -.. note:: The default value is ``false``. Enable deterministic mode if reproducibility is more important than performance. - -Dual Initial Point -"""""""""""""""""" - -``CUOPT_BARRIER_DUAL_INITIAL_POINT`` controls the method used to compute the dual initial point for the barrier solver. The choice of initial point will affect the number of iterations performed by barrier. - -* ``-1``: Automatic (default) - cuOpt selects the best method -* ``0``: Use an initial point from a heuristic approach based on the paper "On Implementing Mehrotra's Predictor–Corrector Interior-Point Method for Linear Programming" (SIAM J. Optimization, 1992) by Lustig, Martsten, Shanno. -* ``1``: Use an initial point from solving a least squares problem that minimizes the norms of the dual variables and reduced costs while statisfying the dual equality constraints. - -.. note:: The default value is ``-1`` (automatic). - -Absolute Primal Tolerance -^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_ABSOLUTE_PRIMAL_TOLERANCE`` controls the absolute primal tolerance used in the primal feasibility check. -Changing this value might have a significant impact on accuracy and runtime if the relative part -(the right-hand side vector b L2 norm) is close to, or equal to, 0. - - -The primal feasibility condition is computed as follows:: - - primal_feasiblity < absolute_primal_tolerance + relative_primal_tolerance * l2_norm(b) - -Default value is ``1e-4``. - - -Relative Primal Tolerance -^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_RELATIVE_PRIMAL_TOLERANCE`` controls the relative primal tolerance used in PDLP's primal feasibility check. -Changing this value has a significant impact on accuracy and runtime. -The primal feasibility condition is computed as follows:: - - primal_feasiblity < absolute_primal_tolerance + relative_primal_tolerance * l2_norm(b) - -.. note:: The default value is ``1e-4``. - -Absolute Dual Tolerance -^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_ABSOLUTE_DUAL_TOLERANCE`` controls the absolute dual tolerance used in PDLP's dual feasibility check. -Changing this value might have a significant impact on accuracy and runtime if the relative part -(the objective vector L2 norm) is close to, or equal to, 0. - -The dual feasibility condition is computed as follows:: - - dual_feasiblity < absolute_dual_tolerance + relative_dual_tolerance * l2_norm(c) - -.. note:: The default value is ``1e-4``. - -Relative Dual Tolerance -^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_RELATIVE_DUAL_TOLERANCE`` controls the relative dual tolerance used in PDLP's dual feasibility check. -Changing this value has a significant impact on accuracy and runtime. -The dual feasibility condition is computed as follows:: - - dual_feasiblity < absolute_dual_tolerance + relative_dual_tolerance * l2_norm(c) - -.. note:: The default value is ``1e-4``. - - -Absolute Gap Tolerance -^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_ABSOLUTE_GAP_TOLERANCE`` controls the absolute gap tolerance used in PDLP's duality gap check. -Changing this value might have a significant impact on accuracy and runtime if the relative part ``(|primal_objective| + |dual_objective|)`` is close to, or equal to, 0. - -The duality gap is computed as follows:: - - duality_gap < absolute_gap_tolerance + relative_gap_tolerance * (|primal_objective| + |dual_objective|) - -.. note:: The default value is ``1e-4``. - - -Relative Gap Tolerance -^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_RELATIVE_GAP_TOLERANCE`` controls the relative gap tolerance used in PDLP's duality gap check. -Changing this value has a significant impact on accuracy and runtime. -The duality gap is computed as follows:: - - duality_gap < absolute_gap_tolerance + relative_gap_tolerance * (|primal_objective| + |dual_objective|) - -.. note:: The default value is ``1e-4``. - -Barrier Iterative Refinement -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_BARRIER_ITERATIVE_REFINEMENT`` controls whether iterative refinement is applied after each barrier iteration to improve solution accuracy. - -* ``0`` (``CUOPT_BARRIER_ITERATIVE_REFINEMENT_OFF``): Disable iterative refinement (default). -* ``1`` (``CUOPT_BARRIER_ITERATIVE_REFINEMENT_ON``): Enable iterative refinement. - -.. note:: The default value is ``0`` (off). - -Barrier Step Scale -^^^^^^^^^^^^^^^^^^^ - -``CUOPT_BARRIER_STEP_SCALE`` controls the scaling factor applied to the step size in the barrier method. -The step scale must be strictly less than 1. A value like 0.9 is conservative, while a value like 0.999 is aggressive. - -.. note:: By default cuOpt selects the step scale automatically. diff --git a/docs/cuopt/source/cuopt-c/convex/convex-c-api.rst b/docs/cuopt/source/cuopt-c/convex/convex-c-api.rst deleted file mode 100644 index 31a4516ca0..0000000000 --- a/docs/cuopt/source/cuopt-c/convex/convex-c-api.rst +++ /dev/null @@ -1,327 +0,0 @@ -cuOpt Convex Optimization C API Reference -========================================= - -This section contains the cuOpt convex optimization C API reference. For MIP-specific functions and callbacks, see :doc:`../mip/mip-c-api`. - -Integer and Floating-Point Types ---------------------------------- - -cuOpt may be built with 32 or 64 bit integer and floating-point types. The C API uses a `typedef` for floating point and integer types to abstract the size of these types. - -.. doxygentypedef:: cuopt_int_t -.. doxygentypedef:: cuopt_float_t - -You may use the following functions to determine the number of bytes used to represent these types in your build - -.. doxygenfunction:: cuOptGetIntSize -.. doxygenfunction:: cuOptGetFloatSize - -Version Information -------------------- - -You may use the following function to get the version of the cuOpt library - -.. doxygenfunction:: cuOptGetVersion - -Status Codes ------------- - -Every function in the C API returns a status code that indicates success or failure. The following status codes are defined - -.. doxygendefine:: CUOPT_SUCCESS -.. doxygendefine:: CUOPT_INVALID_ARGUMENT -.. doxygendefine:: CUOPT_MPS_FILE_ERROR -.. doxygendefine:: CUOPT_MPS_PARSE_ERROR -.. doxygendefine:: CUOPT_VALIDATION_ERROR -.. doxygendefine:: CUOPT_OUT_OF_MEMORY -.. doxygendefine:: CUOPT_RUNTIME_ERROR - -Optimization Problem --------------------- - -An optimization problem is represented via a `cuOptOptimizationProblem` - -.. doxygentypedef:: cuOptOptimizationProblem - -Optimization problems can be created, loaded, or written via the following functions: - -.. doxygenfunction:: cuOptReadProblem -.. doxygenfunction:: cuOptWriteProblem -.. doxygenfunction:: cuOptCreateProblem -.. doxygenfunction:: cuOptCreateRangedProblem - -.. note:: - ``cuOptCreateQuadraticProblem`` and ``cuOptCreateQuadraticRangedProblem`` are deprecated. - Prefer ``cuOptCreateProblem`` or ``cuOptCreateRangedProblem`` followed by - ``cuOptSetQuadraticObjective``. - -For problems with quadratic objectives, first create a problem, and then use - -.. doxygenfunction:: cuOptSetQuadraticObjective - -For problems with quadratic constraints, first create a problem, and then use - -.. doxygenfunction:: cuOptAddQuadraticConstraint - -.. note:: - Support for quadratic constraints is currently in **beta**. ``cuOptAddQuadraticConstraint`` - supports three types of quadratic constraints: - - **Convex quadratic constraints** of the form ``x^T Q x + d^T x <= alpha`` where ``H=(Q+Q^T)/2`` is a symmetric positive semidefinite matrix. `Q` need not be symmetric. - - **Second-order cone constraints** of the form ``sum_{i=1}^n x_i^2 <= x_0``, ``x_0 >= 0`` - - **Rotated second-order cone constraints** of the form ``sum_{i=2}^n x_i^2 - 2 * x_0 * x_1 <= 0``, ``x_0 >= 0``, ``x_1 >= 0`` (supply ``Q[x_0, x_1] = -2`` in COO, or ``-x_0*x_1`` for the ``<= x_0*x_1`` cone form; see :doc:`/convex-features`) - - Only ``CUOPT_LESS_THAN`` and ``CUOPT_GREATER_THAN`` sense is supported; equality constraints are not supported. - -A optimization problem must be destroyed with the following function - -.. doxygenfunction:: cuOptDestroyProblem - -Certain constants are needed to define an optimization problem. These constants are described below. - -Objective Sense Constants -------------------------- - -These constants are used to define the objective sense in the :c:func:`cuOptCreateProblem` and :c:func:`cuOptCreateRangedProblem` functions. - -.. doxygendefine:: CUOPT_MINIMIZE -.. doxygendefine:: CUOPT_MAXIMIZE - -Constraint Sense Constants --------------------------- - -These constants are used to define the constraint sense in the :c:func:`cuOptCreateProblem` and :c:func:`cuOptCreateRangedProblem` functions. - -.. doxygendefine:: CUOPT_LESS_THAN -.. doxygendefine:: CUOPT_GREATER_THAN -.. doxygendefine:: CUOPT_EQUAL - -Variable Type Constants ------------------------ - -These constants are used to define the the variable type in the :c:func:`cuOptCreateProblem` and :c:func:`cuOptCreateRangedProblem` functions. - -.. doxygendefine:: CUOPT_CONTINUOUS -.. doxygendefine:: CUOPT_INTEGER -.. doxygendefine:: CUOPT_SEMI_CONTINUOUS - -Infinity Constant ------------------ - -This constant may be used to represent infinity in the :c:func:`cuOptCreateProblem` and :c:func:`cuOptCreateRangedProblem` functions. - -.. doxygendefine:: CUOPT_INFINITY - -File Format Constants ---------------------- - -These constants are used to specify the output file format in :c:func:`cuOptWriteProblem`. - -.. doxygendefine:: CUOPT_FILE_FORMAT_MPS - -Querying an Optimization Problem --------------------------------- - -The following functions may be used to get information about an `cuOptimizationProblem` - -.. doxygenfunction:: cuOptGetNumConstraints -.. doxygenfunction:: cuOptGetNumVariables -.. doxygenfunction:: cuOptGetObjectiveSense -.. doxygenfunction:: cuOptGetObjectiveOffset -.. doxygenfunction:: cuOptGetObjectiveCoefficients -.. doxygenfunction:: cuOptGetNumNonZeros -.. doxygenfunction:: cuOptGetConstraintMatrix -.. doxygenfunction:: cuOptGetConstraintSense -.. doxygenfunction:: cuOptGetConstraintRightHandSide -.. doxygenfunction:: cuOptGetConstraintLowerBounds -.. doxygenfunction:: cuOptGetConstraintUpperBounds -.. doxygenfunction:: cuOptGetVariableLowerBounds -.. doxygenfunction:: cuOptGetVariableUpperBounds -.. doxygenfunction:: cuOptGetVariableTypes -.. doxygenfunction:: cuOptIsMIP - - -Solver Settings ---------------- - -Settings are used to configure the LP/MIP solvers. All settings are stored in a `cuOptSolverSettings` object. - - -.. doxygentypedef:: cuOptSolverSettings - -A `cuOptSolverSettings` object is created with `cuOptCreateSolverSettings` - -.. doxygenfunction:: cuOptCreateSolverSettings - -When you are done with a solve you should destroy a `cuOptSolverSettings` object with - -.. doxygenfunction:: cuOptDestroySolverSettings - - -Setting Parameters ------------------- -The following functions are used to set and get parameters. You can find more details on the available parameters in the :doc:`Convex Optimization Settings <../../convex-settings>` section. - -.. doxygenfunction:: cuOptSetParameter -.. doxygenfunction:: cuOptGetParameter -.. doxygenfunction:: cuOptSetIntegerParameter -.. doxygenfunction:: cuOptGetIntegerParameter -.. doxygenfunction:: cuOptSetFloatParameter -.. doxygenfunction:: cuOptGetFloatParameter - -.. _parameter-constants: - -Parameter Constants -------------------- - -These constants are used as parameter names in the :c:func:`cuOptSetParameter`, :c:func:`cuOptGetParameter`, and similar functions. For more details on the available parameters, see the :doc:`Convex Optimization Settings <../../convex-settings>` and :doc:`MIP Settings <../../mip-settings>` sections. - -.. Convex optimization (LP/QP/QCQP/SOCP) parameter string constants -.. doxygendefine:: CUOPT_ABSOLUTE_DUAL_TOLERANCE -.. doxygendefine:: CUOPT_RELATIVE_DUAL_TOLERANCE -.. doxygendefine:: CUOPT_ABSOLUTE_PRIMAL_TOLERANCE -.. doxygendefine:: CUOPT_RELATIVE_PRIMAL_TOLERANCE -.. doxygendefine:: CUOPT_ABSOLUTE_GAP_TOLERANCE -.. doxygendefine:: CUOPT_RELATIVE_GAP_TOLERANCE -.. doxygendefine:: CUOPT_INFEASIBILITY_DETECTION -.. doxygendefine:: CUOPT_STRICT_INFEASIBILITY -.. doxygendefine:: CUOPT_PRIMAL_INFEASIBLE_TOLERANCE -.. doxygendefine:: CUOPT_DUAL_INFEASIBLE_TOLERANCE -.. doxygendefine:: CUOPT_ITERATION_LIMIT -.. doxygendefine:: CUOPT_TIME_LIMIT -.. doxygendefine:: CUOPT_PDLP_SOLVER_MODE -.. doxygendefine:: CUOPT_METHOD -.. doxygendefine:: CUOPT_PER_CONSTRAINT_RESIDUAL -.. doxygendefine:: CUOPT_SAVE_BEST_PRIMAL_SO_FAR -.. doxygendefine:: CUOPT_FIRST_PRIMAL_FEASIBLE -.. doxygendefine:: CUOPT_LOG_FILE -.. doxygendefine:: CUOPT_PRESOLVE -.. doxygendefine:: CUOPT_LOG_TO_CONSOLE -.. doxygendefine:: CUOPT_CROSSOVER -.. doxygendefine:: CUOPT_FOLDING -.. doxygendefine:: CUOPT_AUGMENTED -.. doxygendefine:: CUOPT_DUALIZE -.. doxygendefine:: CUOPT_ORDERING -.. doxygendefine:: CUOPT_ELIMINATE_DENSE_COLUMNS -.. doxygendefine:: CUOPT_CUDSS_DETERMINISTIC -.. doxygendefine:: CUOPT_BARRIER_DUAL_INITIAL_POINT -.. doxygendefine:: CUOPT_BARRIER_ITERATIVE_REFINEMENT -.. doxygendefine:: CUOPT_BARRIER_STEP_SCALE -.. doxygendefine:: CUOPT_DUAL_POSTSOLVE -.. doxygendefine:: CUOPT_SOLUTION_FILE -.. doxygendefine:: CUOPT_NUM_CPU_THREADS -.. doxygendefine:: CUOPT_NUM_GPUS -.. doxygendefine:: CUOPT_USER_PROBLEM_FILE -.. doxygendefine:: CUOPT_PDLP_PRECISION - -.. _pdlp-solver-mode-constants: - -PDLP Solver Mode Constants --------------------------- - -These constants are used to configure `CUOPT_PDLP_SOLVER_MODE` via :c:func:`cuOptSetIntegerParameter`. - -.. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_STABLE1 -.. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_STABLE2 -.. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_STABLE3 -.. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_METHODICAL1 -.. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_FAST1 - -.. _pdlp-precision-constants: - -PDLP Precision Constants ------------------------- - -These constants are used to configure `CUOPT_PDLP_PRECISION` via :c:func:`cuOptSetIntegerParameter`. - -.. doxygendefine:: CUOPT_PDLP_DEFAULT_PRECISION -.. doxygendefine:: CUOPT_PDLP_SINGLE_PRECISION -.. doxygendefine:: CUOPT_PDLP_DOUBLE_PRECISION -.. doxygendefine:: CUOPT_PDLP_MIXED_PRECISION - -.. _method-constants: - -Method Constants ----------------- - -These constants are used to configure `CUOPT_METHOD` via :c:func:`cuOptSetIntegerParameter`. - -.. doxygendefine:: CUOPT_METHOD_CONCURRENT -.. doxygendefine:: CUOPT_METHOD_PDLP -.. doxygendefine:: CUOPT_METHOD_DUAL_SIMPLEX -.. doxygendefine:: CUOPT_METHOD_BARRIER -.. doxygendefine:: CUOPT_METHOD_UNSET - -.. _barrier-iterative-refinement-constants: - -Barrier Iterative Refinement Constants ---------------------------------------- - -These constants are used to configure `CUOPT_BARRIER_ITERATIVE_REFINEMENT` via :c:func:`cuOptSetIntegerParameter`. - -.. doxygendefine:: CUOPT_BARRIER_ITERATIVE_REFINEMENT_OFF -.. doxygendefine:: CUOPT_BARRIER_ITERATIVE_REFINEMENT_ON - - -Warm Start ----------- - -For LP problems solved with PDLP, primal and dual warm start vectors may be provided: - -.. doxygenfunction:: cuOptSetInitialPrimalSolution -.. doxygenfunction:: cuOptSetInitialDualSolution - -.. note:: - For MIP warm start (MIP starts), see :doc:`../mip/mip-c-api`. - - -Solving an LP or MIP --------------------- - -LP and MIP solves are performed by calling the `cuOptSolve` function - -.. doxygenfunction:: cuOptSolve - - -Solution --------- - -The output of a solve is a `cuOptSolution` object. - -.. doxygentypedef:: cuOptSolution - -The following functions may be used to access information from a `cuOptSolution` - -.. doxygenfunction:: cuOptGetTerminationStatus -.. doxygenfunction:: cuOptGetErrorStatus -.. doxygenfunction:: cuOptGetErrorString -.. doxygenfunction:: cuOptGetPrimalSolution -.. doxygenfunction:: cuOptGetObjectiveValue -.. doxygenfunction:: cuOptGetSolveTime -.. doxygenfunction:: cuOptGetMIPGap -.. doxygenfunction:: cuOptGetSolutionBound -.. doxygenfunction:: cuOptGetDualSolution -.. doxygenfunction:: cuOptGetDualObjectiveValue -.. doxygenfunction:: cuOptGetReducedCosts - -When you are finished with a `cuOptSolution` object you should destory it with - -.. doxygenfunction:: cuOptDestroySolution - -Termination Status Constants ----------------------------- - -These constants define the termination status received from the :c:func:`cuOptGetTerminationStatus` function. - -.. LP/MIP termination status constants -.. doxygendefine:: CUOPT_TERMINATION_STATUS_NO_TERMINATION -.. doxygendefine:: CUOPT_TERMINATION_STATUS_OPTIMAL -.. doxygendefine:: CUOPT_TERMINATION_STATUS_INFEASIBLE -.. doxygendefine:: CUOPT_TERMINATION_STATUS_UNBOUNDED -.. doxygendefine:: CUOPT_TERMINATION_STATUS_ITERATION_LIMIT -.. doxygendefine:: CUOPT_TERMINATION_STATUS_TIME_LIMIT -.. doxygendefine:: CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR -.. doxygendefine:: CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE -.. doxygendefine:: CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND -.. doxygendefine:: CUOPT_TERMINATION_STATUS_CONCURRENT_LIMIT -.. doxygendefine:: CUOPT_TERMINATION_STATUS_WORK_LIMIT -.. doxygendefine:: CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE diff --git a/docs/cuopt/source/cuopt-c/convex/convex-examples.rst b/docs/cuopt/source/cuopt-c/convex/convex-examples.rst deleted file mode 100644 index a57cd67b8a..0000000000 --- a/docs/cuopt/source/cuopt-c/convex/convex-examples.rst +++ /dev/null @@ -1,468 +0,0 @@ -================================== -Convex Optimization C API Examples -================================== - - -LP Example With Data --------------------- - -This example demonstrates how to use the LP solver in C. More details on the API can be found in :doc:`C API `. - -The example code is available at ``examples/cuopt-c/lp/simple_lp_example.c`` (:download:`download `): - -.. literalinclude:: examples/simple_lp_example.c - :language: c - :linenos: - -It is necessary to have the path for include and library dirs ready, if you know the paths, please add them to the path variables directly. Otherwise, run the following commands to find the path and assign it to the path variables. -The following commands are for Linux and might fail in cases where the cuopt library is not installed or there are multiple cuopt libraries in the system. - -If you have built it locally, libcuopt.so will be in the build directory ``cpp/build`` and include directoy would be ``cpp/include``. - -.. code-block:: bash - - # Find the cuopt header file and assign to INCLUDE_PATH - INCLUDE_PATH=$(find / -name "cuopt_c.h" -path "*/linear_programming/*" -printf "%h\n" | sed 's/\/linear_programming//' 2>/dev/null) - # Find the libcuopt library and assign to LIBCUOPT_LIBRARY_PATH - LIBCUOPT_LIBRARY_PATH=$(find / -name "libcuopt.so" 2>/dev/null) - - -Build and run the example - -.. code-block:: bash - - # Build and run the example - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o simple_lp_example simple_lp_example.c -lcuopt - ./simple_lp_example - - - -You should see the following output: - -.. code-block:: bash - :caption: Output - - Creating and solving simple LP problem... - Solving a problem with 2 constraints 2 variables (0 integers) and 4 nonzeros - Objective offset 0.000000 scaling_factor 1.000000 - Running concurrent - - Dual simplex finished in 0.00 seconds - Iter Primal Obj. Dual Obj. Gap Primal Res. Dual Res. Time - 0 +0.00000000e+00 +0.00000000e+00 0.00e+00 0.00e+00 2.00e-01 0.011s - PDLP finished - Concurrent time: 0.013s - Solved with dual simplex - Status: Optimal Objective: -3.60000000e-01 Iterations: 1 Time: 0.013s - - Results: - -------- - Termination status: Optimal (1) - Solve time: 0.000013 seconds - Objective value: -0.360000 - - Primal Solution: Solution variables - x1 = 1.800000 - x2 = 0.000000 - - Test completed successfully! - - -LP Example With MPS File ------------------------- - -This example demonstrates how to use the cuOpt linear programming solver in C to solve an MPS file. - -The same ``cuOptReadProblem`` call also accepts **LP** format files. The -format is dispatched from the filename extension (case-insensitive): -``.lp`` / ``.lp.gz`` / ``.lp.bz2`` → LP parser; ``.mps`` / ``.qps`` and -their ``.gz`` / ``.bz2`` variants → MPS parser. Unknown extensions are -rejected. See :ref:`lp-file-example-c` for an LP counterpart. - -The example code is available at ``examples/cuopt-c/lp/mps_file_example.c`` (:download:`download `): - -.. literalinclude:: examples/mps_file_example.c - :language: c - :linenos: - -It is necessary to have the path for include and library dirs ready, if you know the paths, please add them to the path variables directly. Otherwise, run the following commands to find the path and assign it to the path variables. -The following commands are for Linux and might fail in cases where the cuopt library is not installed or there are multiple cuopt libraries in the system. - -If you have built it locally, libcuopt.so will be in the build directory ``cpp/build`` and include directoy would be ``cpp/include``. - -.. code-block:: bash - - # Find the cuopt header file and assign to INCLUDE_PATH - INCLUDE_PATH=$(find / -name "cuopt_c.h" -path "*/linear_programming/*" -printf "%h\n" | sed 's/\/linear_programming//' 2>/dev/null) - # Find the libcuopt library and assign to LIBCUOPT_LIBRARY_PATH - LIBCUOPT_LIBRARY_PATH=$(find / -name "libcuopt.so" 2>/dev/null) - -A sample MPS file (:download:`download sample.mps `): - -.. literalinclude:: examples/sample.mps - :language: text - :linenos: - -Build and run the example - -.. code-block:: bash - - # Build and run the example - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o mps_file_example mps_file_example.c -lcuopt - ./mps_file_example sample.mps - - -You should see the following output: - -.. code-block:: bash - :caption: Output - - Reading and solving MPS file: sample.mps - Solving a problem with 2 constraints 2 variables (0 integers) and 4 nonzeros - Objective offset 0.000000 scaling_factor 1.000000 - Running concurrent - - Dual simplex finished in 0.00 seconds - Iter Primal Obj. Dual Obj. Gap Primal Res. Dual Res. Time - 0 +0.00000000e+00 +0.00000000e+00 0.00e+00 0.00e+00 2.00e-01 0.012s - PDLP finished - Concurrent time: 0.014s - Solved with dual simplex - Status: Optimal Objective: -3.60000000e-01 Iterations: 1 Time: 0.014s - - Results: - -------- - Number of variables: 2 - Termination status: Optimal (1) - Solve time: 0.000014 seconds - Objective value: -0.360000 - - Primal Solution: First 10 solution variables (or fewer if less exist): - x1 = 1.800000 - x2 = 0.000000 - - Solver completed successfully! - - -.. _lp-file-example-c: - -LP Example With LP File ------------------------ - -``cuOptReadProblem`` also accepts LP format files. The same function is -used — it dispatches on the file extension (case-insensitive): -``.lp`` / ``.lp.gz`` / ``.lp.bz2`` → LP parser; ``.mps`` / ``.qps`` and -their ``.gz`` / ``.bz2`` variants → MPS parser; unknown extensions are -rejected. See the ``read_lp`` declaration in -``cuopt/mathematical_optimization/io/parser.hpp`` for the supported subset of -the LP format. - -The example code is available at ``examples/cuopt-c/lp/lp_file_example.c`` (:download:`download `): - -.. literalinclude:: examples/lp_file_example.c - :language: c - :linenos: - -A sample LP file (:download:`download sample.lp `), -equivalent to the MPS sample above: - -.. literalinclude:: examples/sample.lp - :language: text - :linenos: - -Build and run the example - -.. code-block:: bash - - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o lp_file_example lp_file_example.c -lcuopt - ./lp_file_example sample.lp - -The output matches the MPS example above (same problem, same objective = -0.36). - - -.. _simple-qp-example-c: - -Simple Quadratic Programming Example ------------------------------------- - -This example demonstrates how to use the cuOpt C API for quadratic programming. - -The example code is available at ``examples/cuopt-c/lp/simple_qp_example.c`` (:download:`download `): - -.. literalinclude:: examples/simple_qp_example.c - :language: c - :linenos: - -Build and run the example - -.. code-block:: bash - - # Build and run the example - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o simple_qp_example simple_qp_example.c -lcuopt - ./simple_qp_example - -You should see the following output: - -.. code-block:: bash - :caption: Output - - Creating and solving simple QP problem... - - Results: - -------- - Termination status: Optimal (1) - Objective value: 0.500000 - - Primal Solution: Solution variables - x1 = 0.500000 - x2 = 0.500000 - - Test completed successfully! - - -.. _simple-socp-example-c: - -Simple Second-Order Cone Programming Example --------------------------------------------- - -This example builds an SOCP directly with the C API: a linear problem is created with :c:func:`cuOptCreateProblem`, then a second-order cone constraint is added with :c:func:`cuOptAddQuadraticConstraint`. It minimizes ``x3`` subject to ``x1 + x2 >= 2`` and the cone ``||(x1, x2)||_2 <= x3``, written as the quadratic inequality ``x1^2 + x2^2 - x3^2 <= 0``. cuOpt detects the second-order cone structure and solves with the barrier method. - -The example code is available at ``examples/cuopt-c/lp/simple_socp_example.c`` (:download:`download `): - -.. literalinclude:: examples/simple_socp_example.c - :language: c - :linenos: - -Build and run the example - -.. code-block:: bash - - # Build and run the example - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o simple_socp_example simple_socp_example.c -lcuopt - ./simple_socp_example - -The optimum is ``x1 = x2 = 1`` and ``x3 = sqrt(2)``: - -.. code-block:: bash - :caption: Output - - Creating and solving simple SOCP problem... - - Results: - -------- - Termination status: Optimal (1) - Objective value: 1.414214 - - Primal Solution: Solution variables - x1 = 1.000000 - x2 = 1.000000 - x3 = 1.414214 - - Test completed successfully! - -Note that dual variables are not currently returned for problems with quadratic constraints. - - -.. _rotated-socp-example-c: - -Rotated Second-Order Cone Example ---------------------------------- - -This example adds a **rotated** cone with :c:func:`cuOptAddQuadraticConstraint`. The -cone ``x1^2 + x2^2 <= x3*x4`` is expressed as ``x1^2 + x2^2 - x3*x4 <= 0``. See :doc:`/convex-features` -for other RSOC forms. -It minimizes ``x3 + x4`` subject to ``x1 + x2 >= 2`` and the rotated cone. - -The example code is available at ``examples/cuopt-c/lp/rotated_socp_example.c`` (:download:`download `): - -.. literalinclude:: examples/rotated_socp_example.c - :language: c - :linenos: - -Build and run the example - -.. code-block:: bash - - # Build and run the example - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o rotated_socp_example rotated_socp_example.c -lcuopt - ./rotated_socp_example - -The optimum is ``x1 = x2 = 1`` and ``x3 = x4 = sqrt(2)``: - -.. code-block:: bash - :caption: Output - - Creating and solving rotated SOCP problem... - - Results: - -------- - Termination status: Optimal (1) - Objective value: 2.828427 - - Primal Solution: Solution variables - x1 = 1.000000 - x2 = 1.000000 - x3 = 1.414214 - x4 = 1.414214 - - Test completed successfully! - -Note that dual variables are not currently returned for problems with quadratic constraints. - - -.. _general-quadratic-example-c: - -General Convex Quadratic Constraint Example -------------------------------------------- - -This example adds a general convex quadratic constraint with -:c:func:`cuOptAddQuadraticConstraint`. Here we add the convex quadratic constraint -``2*x^2 + 2*x*y + 2*y^2 <= 6``. Note that the quadratic matrix Q that encodes this -constraint need not be symmetric. Here the term ``2*x*y`` is supplied as a single entry ``Q[0,1] = 2``. - -The example code is available at ``examples/cuopt-c/lp/general_quadratic_example.c`` (:download:`download `): - -.. literalinclude:: examples/general_quadratic_example.c - :language: c - :linenos: - -Build and run the example - -.. code-block:: bash - - # Build and run the example - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o general_quadratic_example general_quadratic_example.c -lcuopt - ./general_quadratic_example - -The optimum is ``x = y = -1``: - -.. code-block:: bash - :caption: Output - - Creating and solving general convex quadratic problem... - - Results: - -------- - Termination status: Optimal (1) - Objective value: -2.000000 - - Primal Solution: Solution variables - x1 = -1.000000 - x2 = -1.000000 - - Test completed successfully! - - -.. _qp-mps-example-c: - -QP Example With MPS File ------------------------- - -The same ``mps_file_example.c`` program also solves quadratic-objective (QP) -problems. A quadratic objective is supplied through a ``QUADOBJ`` section, which -holds the (upper-triangular) entries of the objective matrix ``Q`` using the -MPS ``1/2 * x^T Q x`` convention. - -A sample QP file (:download:`download qp_sample.mps `), -which minimizes ``x^2 + y^2`` subject to ``x + y >= 1``: - -.. literalinclude:: examples/qp_sample.mps - :language: text - :linenos: - -Run it with the same binary: - -.. code-block:: bash - - ./mps_file_example qp_sample.mps - -cuOpt detects the quadratic objective and solves with the barrier method. The -optimum is ``x = y = 0.5`` with objective ``0.5``: - -.. code-block:: bash - :caption: Output (barrier iteration log abbreviated) - - Reading and solving MPS file: qp_sample.mps - Problem has a quadratic objective. Solving with barrier. - Barrier solver: 1 constraints, 3 variables, 3 nonzeros - Quadratic objective matrix : 2 nonzeros - ... - Optimal solution found in 11 iterations and 0.188s - Objective +5.00000008e-01 - Barrier finished in 0.19 seconds - - Results: - -------- - Number of variables: 2 - Termination status: Optimal (1) - Solve time: 0.190449 seconds - Objective value: 0.500000 - - Primal Solution: First 10 solution variables (or fewer if less exist): - x1 = 0.500000 - x2 = 0.500000 - - Solver completed successfully! - - -.. _socp-mps-example-c: - -SOCP Example With MPS File --------------------------- - -Second-order cone (SOCP) constraints are expressed through ``QCMATRIX`` sections -— one per quadratic constraint — each holding the **full, symmetric** entries of -that constraint's quadratic matrix with a zero right-hand side. Both standard and -rotated cones are supported; see the SOCP section of -:doc:`LP/QP/QCQP/SOCP Features ` for the cone forms. - -A sample SOCP file (:download:`download socp_sample.mps `) -minimizes ``s + p + q`` subject to ``a + b >= 2`` and two cones: - -- standard cone ``||(a, b)||_2 <= s`` (row ``QCSTD``, written ``a^2 + b^2 - s^2 <= 0, s >= 0``) -- rotated cone ``a^2 + b^2 <= p * q, p >= 0, q >= 0`` (row ``QCROT``) - -.. literalinclude:: examples/socp_sample.mps - :language: text - :linenos: - -The rotated cone uses symmetric cross-term halves ``P Q -0.5`` and ``Q P -0.5`` in the -QCMATRIX section. Run it -with the same binary: - -.. code-block:: bash - - ./mps_file_example socp_sample.mps - -cuOpt detects the quadratic constraints, converts them to second-order cones, and -solves with the barrier method. The optimum is ``a = b = 1`` and -``s = p = q = sqrt(2) ≈ 1.414214`` with objective ``3*sqrt(2) ≈ 4.242641``. In the -output below, ``x1..x5`` are ``a, b, s, p, q`` in column order: - -.. code-block:: bash - :caption: Output (barrier iteration log abbreviated) - - Reading and solving MPS file: socp_sample.mps - Problem has 2 quadratic constraints. Converting to second-order cones and solving with barrier. - Barrier solver: 5 constraints, 10 variables, 13 nonzeros - Second-order cones : 2 - ... - Optimal solution found in 9 iterations and 0.126s - Objective +4.24264071e+00 - Barrier finished in 0.13 seconds - - Results: - -------- - Number of variables: 5 - Termination status: Optimal (1) - Solve time: 0.128689 seconds - Objective value: 4.242641 - - Primal Solution: First 10 solution variables (or fewer if less exist): - x1 = 1.000000 - x2 = 1.000000 - x3 = 1.414214 - x4 = 1.414214 - x5 = 1.414214 - - Solver completed successfully! diff --git a/docs/cuopt/source/cuopt-c/convex/index.rst b/docs/cuopt/source/cuopt-c/convex/index.rst deleted file mode 100644 index 34702b67e8..0000000000 --- a/docs/cuopt/source/cuopt-c/convex/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -Convex Optimization (LP/QP/QCQP/SOCP) -===================================== - -This section contains details on the cuOpt convex optimization C API. - -.. toctree:: - :maxdepth: 3 - :caption: LP/QP/QCQP/SOCP C API - :name: LP/QP/QCQP/SOCP C API - :titlesonly: - - convex-c-api.rst - ../../convex-settings.rst - convex-examples.rst diff --git a/docs/cuopt/source/cuopt-c/index.rst b/docs/cuopt/source/cuopt-c/index.rst deleted file mode 100644 index 67e01ad43c..0000000000 --- a/docs/cuopt/source/cuopt-c/index.rst +++ /dev/null @@ -1,28 +0,0 @@ -C API -===== - -NVIDIA cuOpt supports a C API for GPU-accelerated optimization that enables users to solve complex optimization problems efficiently. The cuOpt C API supports convex optimization (LP, QP, QCQP, and SOCP) and mixed-integer programming (MIP). - -.. toctree:: - :maxdepth: 3 - :caption: C API Overview - :name: C API Overview - :titlesonly: - - quick-start.rst - -.. toctree:: - :maxdepth: 3 - :caption: Convex Optimization (LP/QP/QCQP/SOCP) - :name: Convex Optimization C API - :titlesonly: - - Convex Optimization - -.. toctree:: - :maxdepth: 3 - :caption: Mixed Integer Programming (MIP) - :name: MIP C API Index - :titlesonly: - - MIP diff --git a/docs/cuopt/source/cuopt-c/mip/index.rst b/docs/cuopt/source/cuopt-c/mip/index.rst deleted file mode 100644 index 4930f067b2..0000000000 --- a/docs/cuopt/source/cuopt-c/mip/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -MIP (Mixed Integer Programming) -================================ - -.. note:: - - The cuOpt MIP solver is in **beta** and under active development. The solver - currently excels at finding high-quality feasible solutions quickly with - GPU-accelerated primal heuristics. Proving feasible solutions optimal remains - under active development. - -This section contains details on the cuOpt MIP C API. - -.. toctree:: - :maxdepth: 3 - :caption: MIP C API - :name: MIP C API - :titlesonly: - - mip-c-api.rst - ../../mip-settings.rst - mip-examples.rst diff --git a/docs/cuopt/source/cuopt-c/mip/mip-c-api.rst b/docs/cuopt/source/cuopt-c/mip/mip-c-api.rst deleted file mode 100644 index 85592bb766..0000000000 --- a/docs/cuopt/source/cuopt-c/mip/mip-c-api.rst +++ /dev/null @@ -1,75 +0,0 @@ -cuOpt MIP C API Reference -========================= - -This section contains the cuOpt MIP C API reference. Functions for problem creation, solver settings, solving, and inspecting solutions are shared with convex optimization and documented in :doc:`../convex/convex-c-api`. - -Warm Start and MIP Start ------------------------- - -For LP problems solved with PDLP, see :doc:`../convex/convex-c-api` for primal and dual warm start. - -For MIP problems, one or more primal solution hints (MIP starts) may be provided: - -.. doxygenfunction:: cuOptAddMIPStart - -MIP Solution Callbacks ------------------------ - -The following callback types and functions allow monitoring and injecting solutions during a MIP solve. - -.. doxygentypedef:: cuOptMIPGetSolutionCallback -.. doxygentypedef:: cuOptMIPSetSolutionCallback - -.. doxygenfunction:: cuOptSetMIPGetSolutionCallback -.. doxygenfunction:: cuOptSetMIPSetSolutionCallback - -.. _mip-parameter-constants: - -MIP Parameter Constants ------------------------ - -These constants configure MIP-specific solver behavior. Use them with :c:func:`cuOptSetParameter`, :c:func:`cuOptSetIntegerParameter`, or :c:func:`cuOptSetFloatParameter`. For shared parameters (time limit, logging, presolve, etc.), see :ref:`parameter-constants` in the convex API reference. - -.. doxygendefine:: CUOPT_NODE_LIMIT -.. doxygendefine:: CUOPT_WORK_LIMIT -.. doxygendefine:: CUOPT_RANDOM_SEED -.. doxygendefine:: CUOPT_PRESOLVE_FILE -.. doxygendefine:: CUOPT_MIP_ABSOLUTE_TOLERANCE -.. doxygendefine:: CUOPT_MIP_RELATIVE_TOLERANCE -.. doxygendefine:: CUOPT_MIP_INTEGRALITY_TOLERANCE -.. doxygendefine:: CUOPT_MIP_ABSOLUTE_GAP -.. doxygendefine:: CUOPT_MIP_RELATIVE_GAP -.. doxygendefine:: CUOPT_MIP_SCALING -.. doxygendefine:: CUOPT_MIP_HEURISTICS_ONLY -.. doxygendefine:: CUOPT_MIP_PRESOLVE -.. doxygendefine:: CUOPT_MIP_DETERMINISM_MODE -.. doxygendefine:: CUOPT_MIP_SYMMETRY -.. doxygendefine:: CUOPT_MIP_PROBING -.. doxygendefine:: CUOPT_MIP_RELIABILITY_BRANCHING -.. doxygendefine:: CUOPT_MIP_CUT_PASSES -.. doxygendefine:: CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS -.. doxygendefine:: CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS -.. doxygendefine:: CUOPT_MIP_KNAPSACK_CUTS -.. doxygendefine:: CUOPT_MIP_FLOW_COVER_CUTS -.. doxygendefine:: CUOPT_MIP_IMPLIED_BOUND_CUTS -.. doxygendefine:: CUOPT_MIP_CLIQUE_CUTS -.. doxygendefine:: CUOPT_MIP_ZERO_HALF_CUTS -.. doxygendefine:: CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS -.. doxygendefine:: CUOPT_MIP_REDUCED_COST_STRENGTHENING -.. doxygendefine:: CUOPT_MIP_OBJECTIVE_STEP -.. doxygendefine:: CUOPT_MIP_CUT_CHANGE_THRESHOLD -.. doxygendefine:: CUOPT_MIP_CUT_MIN_ORTHOGONALITY -.. doxygendefine:: CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING -.. doxygendefine:: CUOPT_MIP_BATCH_PDLP_RELIABILITY_BRANCHING -.. doxygendefine:: CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITERATION_LIMIT -.. doxygendefine:: CUOPT_MIP_SEMICONTINUOUS_BIG_M - -.. _mip-determinism-mode-constants: - -MIP Determinism Mode Constants ------------------------------- - -These constants are used to configure `CUOPT_MIP_DETERMINISM_MODE` via :c:func:`cuOptSetIntegerParameter`. - -.. doxygendefine:: CUOPT_MODE_OPPORTUNISTIC -.. doxygendefine:: CUOPT_MODE_DETERMINISTIC diff --git a/docs/cuopt/source/cuopt-c/mip/mip-examples.rst b/docs/cuopt/source/cuopt-c/mip/mip-examples.rst deleted file mode 100644 index 602c387340..0000000000 --- a/docs/cuopt/source/cuopt-c/mip/mip-examples.rst +++ /dev/null @@ -1,162 +0,0 @@ -MILP C API Examples -=================== - - -Example With Data ------------------ - -This example demonstrates how to use the MILP solver in C. More details on the API can be found in :doc:`MIP C API `. - -The example code is available at ``../lp-milp/examples/simple_milp_example.c`` (:download:`download `): - -.. literalinclude:: examples/simple_milp_example.c - :language: c - :linenos: - -It is necessary to have the path for include and library dirs ready, if you know the paths, please add them to the path variables directly. Otherwise, run the following commands to find the path and assign it to the path variables. -The following commands are for Linux and might fail in cases where the cuopt library is not installed or there are multiple cuopt libraries in the system. - -If you have built it locally, libcuopt.so will be in the build directory ``cpp/build`` and include directoy would be ``cpp/include``. - -.. code-block:: bash - - # Find the cuopt header file and assign to INCLUDE_PATH - INCLUDE_PATH=$(find / -name "cuopt_c.h" -path "*/linear_programming/*" -printf "%h\n" | sed 's/\/linear_programming//' 2>/dev/null) - # Find the libcuopt library directory and assign to LIBCUOPT_LIBRARY_PATH - LIBCUOPT_LIBRARY_PATH=$(dirname $(find / -name "libcuopt.so" 2>/dev/null)) - - -Build and run the example - -.. code-block:: bash - - # Build and run the example - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o simple_milp_example simple_milp_example.c -lcuopt - ./simple_milp_example - - - -You should see the following output: - -.. code-block:: bash - :caption: Output - - Creating and solving simple LP problem... - Solving a problem with 2 constraints 2 variables (1 integers) and 4 nonzeros - Objective offset 0.000000 scaling_factor 1.000000 - After trivial presolve updated 2 constraints 2 variables - Running presolve! - After trivial presolve updated 2 constraints 2 variables - Solving LP root relaxation - Scaling matrix. Maximum column norm 1.046542e+00 - Dual Simplex Phase 1 - Dual feasible solution found. - Dual Simplex Phase 2 - Iter Objective Primal Infeas Perturb Time - 1 -2.00000000e-01 1.46434160e+00 0.00e+00 0.00 - - Root relaxation solution found in 2 iterations and 0.00s - Root relaxation objective -2.00000000e-01 - - Optimal solution found at root node. Objective -2.0000000000000001e-01. Time 0.00. - B&B added a solution to population, solution queue size 0 with objective -0.2 - Solution objective: -0.200000 , relative_mip_gap 0.000000 solution_bound -0.200000 presolve_time 0.041144 total_solve_time 0.000000 max constraint violation 0.000000 max int violation 0.000000 max var bounds violation 0.000000 nodes 0 simplex_iterations 0 - - Results: - -------- - Termination status: Optimal (1) - Solve time: 0.000000 seconds - Objective value: -0.200000 - - Solution: - x1 = 1.000000 - x2 = 0.000000 - - Test completed successfully! - - -Example With MPS File ---------------------- - -This example demonstrates how to use the cuOpt solver in C to solve an MPS file. - -The example code is available at ``examples/milp_mps_example.c`` (:download:`download `): - -.. literalinclude:: examples/milp_mps_example.c - :language: c - :linenos: - -It is necessary to have the path for include and library dirs ready, if you know the paths, please add them to the path variables directly. Otherwise, run the following commands to find the path and assign it to the path variables. -The following commands are for Linux and might fail in cases where the cuopt library is not installed or there are multiple cuopt libraries in the system. - -If you have built it locally, libcuopt.so will be in the build directory ``cpp/build`` and include directoy would be ``cpp/include``. - -.. code-block:: bash - - # Find the cuopt header file and assign to INCLUDE_PATH - INCLUDE_PATH=$(find / -name "cuopt_c.h" -path "*/linear_programming/*" -printf "%h\n" | sed 's/\/linear_programming//' 2>/dev/null) - # Find the libcuopt library directory and assign to LIBCUOPT_LIBRARY_PATH - LIBCUOPT_LIBRARY_PATH=$(dirname $(find / -name "libcuopt.so" 2>/dev/null)) - -A sample MILP MPS file (:download:`download mip_sample.mps `): - -.. literalinclude:: examples/mip_sample.mps - :language: text - :linenos: - -Build and run the example - -.. code-block:: bash - - # Build and run the example - gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o milp_mps_example milp_mps_example.c -lcuopt - ./milp_mps_example mip_sample.mps - - -You should see the following output: - -.. code-block:: bash - :caption: Output - - Reading and solving MPS file: sample.mps - Solving a problem with 3 constraints 2 variables (2 integers) and 6 nonzeros - Objective offset 0.000000 scaling_factor 1.000000 - After trivial presolve updated 3 constraints 2 variables - Running presolve! - After trivial presolve updated 3 constraints 2 variables - Solving LP root relaxation - Scaling matrix. Maximum column norm 1.225464e+00 - Dual Simplex Phase 1 - Dual feasible solution found. - Dual Simplex Phase 2 - Iter Objective Primal Infeas Perturb Time - 1 -3.04000000e+01 7.57868205e+00 0.00e+00 0.00 - - Root relaxation solution found in 3 iterations and 0.00s - Root relaxation objective -3.01818182e+01 - - Strong branching on 2 fractional variables - | Explored | Unexplored | Objective | Bound | Depth | Iter/Node | Gap | Time - 0 1 +inf -3.018182e+01 1 0.0e+00 - 0.00 - B 3 1 -2.700000e+01 -2.980000e+01 2 6.7e-01 10.4% 0.00 - B&B added a solution to population, solution queue size 0 with objective -27 - B 4 0 -2.800000e+01 -2.980000e+01 2 7.5e-01 6.4% 0.00 - B&B added a solution to population, solution queue size 1 with objective -28 - Explored 4 nodes in 0.00s. - Absolute Gap 0.000000e+00 Objective -2.8000000000000004e+01 Lower Bound -2.8000000000000004e+01 - Optimal solution found. - Generated fast solution in 0.136067 seconds with objective -28.000000 - Solution objective: -28.000000 , relative_mip_gap 0.000000 solution_bound -28.000000 presolve_time 0.039433 total_solve_time 0.000000 max constraint violation 0.000000 max int violation 0.000000 max var bounds violation 0.000000 nodes 4 simplex_iterations 3 - - Results: - -------- - Number of variables: 2 - Termination status: Optimal (1) - Solve time: 0.000000 seconds - Objective value: -28.000000 - - Solution: - x1 = 4.000000 - x2 = 0.000000 - - Solver completed successfully! diff --git a/docs/cuopt/source/cuopt-cli/cli-examples.rst b/docs/cuopt/source/cuopt-cli/cli-examples.rst deleted file mode 100644 index 62e794285b..0000000000 --- a/docs/cuopt/source/cuopt-cli/cli-examples.rst +++ /dev/null @@ -1,107 +0,0 @@ -Examples -======== - -Input File Format -################# - -``cuopt_cli`` accepts both **MPS** and **LP** format input files. The -format is dispatched automatically from the file extension -(case-insensitive): - -- ``*.lp``, ``*.lp.gz``, ``*.lp.bz2`` → parsed as LP format -- ``*.mps``, ``*.mps.gz``, ``*.mps.bz2`` (and the equivalent ``*.qps`` - variants) → parsed as MPS / QPS - -Any other extension (including no extension) is rejected with an error -listing the supported suffixes. See ``read`` in -``cuopt/mathematical_optimization/io/parser.hpp`` (and the Python -:func:`~cuopt.linear_programming.io.Read` wrapper). - - -Basic Usage -########### - -To solve a simple LP problem using cuopt_cli: - -:download:`basic_lp_example.sh ` - -.. literalinclude:: examples/lp/examples/basic_lp_example.sh - :language: bash - :linenos: - -This should give you the following output: - -.. code-block:: bash - :caption: Output - - Running file sample.mps - Solving a problem with 2 constraints 2 variables (0 integers) and 4 nonzeros - Objective offset 0.000000 scaling_factor 1.000000 - Running concurrent - - Dual simplex finished in 0.00 seconds - Iter Primal Obj. Dual Obj. Gap Primal Res. Dual Res. Time - 0 +0.00000000e+00 +0.00000000e+00 0.00e+00 0.00e+00 2.00e-01 0.033s - PDLP finished - Concurrent time: 0.036s - Solved with dual simplex - Status: Optimal Objective: -3.60000000e-01 Iterations: 1 Time: 0.036s - - -Mixed Integer Programming Example -################################# - -Here's an example of solving a Mixed Integer Programming (MIP) problem using the CLI: - -:download:`basic_milp_example.sh ` - -.. literalinclude:: examples/milp/examples/basic_milp_example.sh - :language: bash - :linenos: - -This should produce output similar to: - -.. code-block:: bash - :caption: Output - - Running file mip_sample.mps - Solving a problem with 3 constraints 2 variables (2 integers) and 6 nonzeros - Objective offset 0.000000 scaling_factor 1.000000 - After trivial presolve updated 3 constraints 2 variables - Running presolve! - After trivial presolve updated 3 constraints 2 variables - Solving LP root relaxation - Scaling matrix. Maximum column norm 1.225464e+00 - Dual Simplex Phase 1 - Dual feasible solution found. - Dual Simplex Phase 2 - Iter Objective Primal Infeas Perturb Time - 1 -3.04000000e+01 7.57868205e+00 0.00e+00 0.00 - - Root relaxation solution found in 3 iterations and 0.00s - Root relaxation objective -3.01818182e+01 - - Strong branching on 2 fractional variables - | Explored | Unexplored | Objective | Bound | Depth | Iter/Node | Gap | Time - 0 1 +inf -3.018182e+01 1 0.0e+00 - 0.00 - B 3 1 -2.700000e+01 -2.980000e+01 2 6.7e-01 10.4% 0.00 - B&B added a solution to population, solution queue size 0 with objective -27 - B 4 0 -2.800000e+01 -2.980000e+01 2 7.5e-01 6.4% 0.00 - B&B added a solution to population, solution queue size 1 with objective -28 - Explored 4 nodes in 0.00s. - Absolute Gap 0.000000e+00 Objective -2.8000000000000004e+01 Lower Bound -2.8000000000000004e+01 - Optimal solution found. - Consuming B&B solutions, solution queue size 2 - Solution objective: -28.000000 , relative_mip_gap 0.000000 solution_bound -28.000000 presolve_time 0.227418 total_solve_time 0.000000 max constraint violation 0.000000 max int violation 0.000000 max var bounds violation 0.000000 nodes 4 simplex_iterations 3 - - -Using Solver Parameters -####################### - -You can customize the solver behavior using various command line parameters. Here's a comprehensive example showing different parameter options: - -:download:`solver_parameters_example.sh ` - -.. literalinclude:: examples/lp/examples/solver_parameters_example.sh - :language: bash - :linenos: diff --git a/docs/cuopt/source/cuopt-cli/index.rst b/docs/cuopt/source/cuopt-cli/index.rst deleted file mode 100644 index 4093db0028..0000000000 --- a/docs/cuopt/source/cuopt-cli/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -Command Line Interface -====================== - -The cuopt_cli is a command-line interface for LP/MILP solvers that accepts MPS, QPS, or LP format files as input models. The format is dispatched automatically from the file extension (case-insensitive): ``.lp`` (with optional ``.gz`` / ``.bz2``) goes to the LP parser, ``.mps`` / ``.qps`` (with optional ``.gz`` / ``.bz2``) goes to the MPS parser, and unknown extensions are rejected. It provides command-line arguments to control all solver settings and parameters when solving linear and mixed-integer programming problems. - -The cuOpt MIP solver is in **beta** and under active development. The solver -currently excels at finding high-quality feasible solutions quickly with -GPU-accelerated primal heuristics. Proving feasible solutions optimal remains -under active development. - -.. toctree:: - :maxdepth: 3 - :caption: Command Line Interface Overview - :name: Command Line Interface Overview - :titlesonly: - - quick-start.rst - -.. toctree:: - :maxdepth: 3 - :caption: Usage - :name: Usage - :titlesonly: - - cli-examples.rst diff --git a/docs/cuopt/source/cuopt-cli/quick-start.rst b/docs/cuopt/source/cuopt-cli/quick-start.rst deleted file mode 100644 index 445ace5c6f..0000000000 --- a/docs/cuopt/source/cuopt-cli/quick-start.rst +++ /dev/null @@ -1,26 +0,0 @@ -================= -Quickstart Guide -================= - -cuopt_cli is built as part of the libcuopt package. Choose your install method below; the selector is pre-set for the CLI (it uses the same libcuopt install). Copy the command and run it in your environment. See :doc:`../install` for all interfaces and options. - -Installation -============ - -.. install-selector:: - :default-iface: cli - -To see all available options and their descriptions: - - -.. code-block:: bash - - cuopt_cli --help - -This will display the complete list of command-line arguments and their usage: - -.. literalinclude:: cuopt-cli-help.txt - :language: text - :linenos: - -Please refer to :doc:`../convex-settings` and :doc:`../mip-settings` for more details on default values and other options. diff --git a/docs/cuopt/source/cuopt-grpc/advanced.rst b/docs/cuopt/source/cuopt-grpc/advanced.rst deleted file mode 100644 index a092f026c3..0000000000 --- a/docs/cuopt/source/cuopt-grpc/advanced.rst +++ /dev/null @@ -1,314 +0,0 @@ -.. - SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - SPDX-License-Identifier: Apache-2.0 - -======================= -Advanced Configuration -======================= - -This page lists **configuration parameters** first, then **usage** walkthroughs (TLS, Docker, private CA). Complete :doc:`quick-start` first (install, plain TCP server, and minimal example). - -For RPC summaries and server behavior, see :doc:`api` and :doc:`grpc-server-architecture`. Example entry points with ``CUOPT_REMOTE_*``: :doc:`examples`. Contributor-only internals: ``cpp/docs/grpc-server-architecture.md`` in the repository. - -Configuration Parameters -======================== - -``cuopt_grpc_server`` (host or explicit container command) ------------------------------------------------------------- - -Run ``cuopt_grpc_server --help`` for the full list. Typical flags (also passable inside ``CUOPT_GRPC_ARGS`` when using the container entrypoint): - -.. code-block:: text - - cuopt_grpc_server [options] - - -p, --port PORT gRPC listen port (default: 5001) - -w, --workers NUM Number of worker processes (default: 1) - --max-message-mb N Max gRPC message size in MiB (default: 256; clamped to [4 KiB, ~2 GiB]) - --max-message-bytes N Max gRPC message size in bytes (exact; min 4096) - --chunk-timeout N Per-chunk timeout in seconds for streaming (0=disabled, default: 60) - --log-to-console Echo solver logs to server console - -v, --verbose Increase verbosity (default: on) - -q, --quiet Reduce verbosity (verbose is the default) - --server-log PATH Path to server operational log file (in addition to console) - - TLS Options: - --tls Enable TLS encryption - --tls-cert PATH Server certificate (PEM) - --tls-key PATH Server private key (PEM) - --tls-root PATH Root CA certificate (for client verification) - --require-client-cert Require client certificate (mTLS) - -NVIDIA cuOpt Container (gRPC via Entrypoint) --------------------------------------------- - -These variables apply when the container **entrypoint** builds a ``cuopt_grpc_server`` command (see *Docker: gRPC server in container* under Usage). If you pass an explicit command after the image name, this table does not apply. - -.. list-table:: - :header-rows: 1 - :widths: 22 18 60 - - * - Variable - - Default - - Description - * - ``CUOPT_SERVER_TYPE`` - - *(unset)* - - Set to ``grpc`` for entrypoint-built gRPC. Unset with no explicit command: **Python REST** server. - * - ``CUOPT_SERVER_PORT`` - - ``5001`` - - Passed as ``--port`` to ``cuopt_grpc_server``. - * - ``CUOPT_GPU_COUNT`` - - *(unset)* - - When set, passed as ``--workers``. When unset, ``--workers`` is omitted (server default, typically 1). - * - ``CUOPT_GRPC_ARGS`` - - *(empty)* - - Extra flags split on **whitespace** and appended (TLS, ``--max-message-mb``, ``--log-to-console``, etc.). Paths with spaces: prefer mounts without spaces or run ``cuopt_grpc_server`` manually with proper quoting. - -The REST server path in the same image still uses ``CUOPT_SERVER_PORT`` for HTTP in other docs; that is separate from the gRPC defaults above. - -Bundled Remote Client (Python, C API, ``cuopt_cli``) ----------------------------------------------------- - -Remote mode is active when **both** ``CUOPT_REMOTE_HOST`` and ``CUOPT_REMOTE_PORT`` are set. A **custom** gRPC client does not read these automatically; it must configure the channel and protos itself (see :doc:`api`). - -.. list-table:: - :header-rows: 1 - :widths: 26 14 18 42 - - * - Variable - - Required - - Default - - Description - * - ``CUOPT_REMOTE_HOST`` - - For remote - - — - - Server hostname or IP - * - ``CUOPT_REMOTE_PORT`` - - For remote - - — - - Server port (e.g. ``5001``) - * - ``CUOPT_TLS_ENABLED`` - - No - - ``0`` - - Non-zero enables TLS on the client - * - ``CUOPT_TLS_ROOT_CERT`` - - If TLS - - — - - PEM path to verify the **server** certificate - * - ``CUOPT_TLS_CLIENT_CERT`` - - mTLS - - — - - Client certificate PEM - * - ``CUOPT_TLS_CLIENT_KEY`` - - mTLS - - — - - Client private key PEM - * - ``CUOPT_CHUNK_SIZE`` - - No - - 16 MiB (lib) - - Chunk size in **bytes** for large transfers (clamped in library code) - * - ``CUOPT_MAX_MESSAGE_BYTES`` - - No - - 256 MiB (lib) - - Client gRPC max message size in **bytes** (clamped in library code) - * - ``CUOPT_GRPC_DEBUG`` - - No - - ``0`` - - Non-zero: extra gRPC client logging - -Usage -===== - -Start the Server with TLS --------------------------- - -Basic (no TLS), plain TCP, is in :doc:`quick-start`. Encrypted server: - -.. code-block:: bash - - cuopt_grpc_server --port 5001 \ - --tls \ - --tls-cert server.crt \ - --tls-key server.key - -mTLS (mutual TLS): - -.. code-block:: bash - - cuopt_grpc_server --port 5001 \ - --tls \ - --tls-cert server.crt \ - --tls-key server.key \ - --tls-root ca.crt \ - --require-client-cert - -How mTLS Works --------------- - -With mTLS the server verifies every client, and the client verifies the server. Trust is based on **Certificate Authorities** (CAs), not individual certificate lists: - -* ``--tls-root ca.crt`` tells the server which CA to trust; any client cert signed by that CA is accepted. The server does not store per-client certificates. -* ``--require-client-cert`` makes client verification **mandatory**. Without it, the server may still allow connections without a client cert. -* On the client, ``CUOPT_TLS_ROOT_CERT`` is the CA that signed the **server** certificate so the client can verify the server. - -Restricting Access with a Private CA ------------------------------------- - -To limit which clients can connect, run your own CA and issue client certs only to authorized actors. - -**1. Create a private CA (one-time):** - -.. code-block:: bash - - openssl genrsa -out ca.key 4096 - openssl req -new -x509 -key ca.key -sha256 -days 3650 \ - -subj "/CN=cuopt-internal-ca" -out ca.crt - -**2. Issue a client certificate:** - -.. code-block:: bash - - openssl genrsa -out client.key 2048 - openssl req -new -key client.key \ - -subj "/CN=team-member-alice" -out client.csr - openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \ - -CAcreateserial -days 365 -sha256 -out client.crt - -Repeat for each authorized client. Keep ``ca.key`` private; distribute ``ca.crt`` to the server and per-client ``client.crt`` + ``client.key`` pairs. - -**3. Issue a server certificate (same CA):** - -.. code-block:: bash - - openssl genrsa -out server.key 2048 - openssl req -new -key server.key \ - -subj "/CN=server.example.com" -out server.csr - - cat > server.ext <`_ so devices are visible inside the container. - -Typical run: - -.. code-block:: bash - - docker run --gpus all -p 5001:5001 \ - -e CUOPT_SERVER_TYPE=grpc \ - nvcr.io/nvidia/cuopt/cuopt:latest-cu12 - -TLS example with a cert volume: - -.. code-block:: bash - - docker run --gpus all -p 5001:5001 \ - -e CUOPT_SERVER_TYPE=grpc \ - -e CUOPT_GRPC_ARGS="--tls --tls-cert /certs/server.crt --tls-key /certs/server.key --log-to-console" \ - -v ./certs:/certs:ro \ - nvcr.io/nvidia/cuopt/cuopt:latest-cu12 - -Bypass the entrypoint: - -.. code-block:: bash - - docker run --gpus all -p 5001:5001 \ - nvcr.io/nvidia/cuopt/cuopt:latest-cu12 \ - cuopt_grpc_server --port 5001 --workers 2 - -Client Environment (Examples) ------------------------------- - -**Required** for remote (see *Bundled remote client* table for all variables): - -.. code-block:: bash - - export CUOPT_REMOTE_HOST= - export CUOPT_REMOTE_PORT=5001 - -**TLS** (optional): - -.. code-block:: bash - - export CUOPT_TLS_ENABLED=1 - export CUOPT_TLS_ROOT_CERT=ca.crt - -For mTLS, also: - -.. code-block:: bash - - export CUOPT_TLS_CLIENT_CERT=client.crt - export CUOPT_TLS_CLIENT_KEY=client.key - -Limitations and Scope -===================== - -* **Problem types** — **LP**, **MILP**, and **QP** are supported on the gRPC remote path. **Routing** (VRP, TSP, PDP) is **not** supported yet; use the :doc:`REST self-hosted server <../cuopt-server/index>` for remote routing until a future release adds routing over ``CuOptRemoteService``. -* **Message size** — Large problems use chunking; very large models can still hit gRPC max message / timeout limits. Tune ``CUOPT_CHUNK_SIZE``, ``CUOPT_MAX_MESSAGE_BYTES``, server ``--max-message-mb``, and solver ``time_limit`` as needed. -* **``CUOPT_GRPC_ARGS``** — Parsed on whitespace only; arguments containing spaces are awkward unless you invoke ``cuopt_grpc_server`` directly. -* **CRL / OCSP** — Not handled by the bundled gRPC TLS stack; use a private CA rotation strategy or a TLS-terminating proxy if you need revocation workflows. - -Troubleshooting -=============== - -.. list-table:: - :header-rows: 1 - :widths: 28 72 - - * - Symptom - - Check - * - Connection refused - - Server running; host/port match; firewalls and Docker port mapping. - * - TLS handshake failure - - ``CUOPT_TLS_ENABLED=1``; correct CA and cert paths; SAN matches server name. - * - Cannot open TLS file - - Path exists and is readable inside the client/server environment (including container mounts). - * - Timeout on large problems - - Increase solver ``time_limit`` and client/server message limits. - -Further Reading -=============== - -* :doc:`quick-start` — Plain TCP quick path. -* :doc:`examples` — Links to Python, C, and CLI example sections (use with ``CUOPT_REMOTE_*`` on the client). -* :doc:`grpc-server-architecture` — Process model and job behavior (operator overview). diff --git a/docs/cuopt/source/cuopt-grpc/api.rst b/docs/cuopt/source/cuopt-grpc/api.rst deleted file mode 100644 index 5706038032..0000000000 --- a/docs/cuopt/source/cuopt-grpc/api.rst +++ /dev/null @@ -1,98 +0,0 @@ -.. - SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - SPDX-License-Identifier: Apache-2.0 - -====================== -gRPC API (Reference) -====================== - -The **CuOptRemoteService** gRPC API is defined in Protocol Buffers under the ``cuopt.remote`` package. Source files in the repository: - -* ``cpp/src/grpc/cuopt_remote_service.proto`` — service and job/chunk/log RPCs -* ``cpp/src/grpc/cuopt_remote.proto`` — LP/MIP problem, settings, and result messages - -Most users do **not** call these RPCs directly: the NVIDIA cuOpt **Python** API, **C API**, and **cuopt_cli** submit jobs using solver APIs plus :doc:`environment variables `. **Custom** clients call ``CuOptRemoteService`` over gRPC using these definitions. This page summarizes the service for custom integrators and debugging. - -Service: ``CuOptRemoteService`` -================================ - -Asynchronous Jobs ------------------ - -.. list-table:: - :header-rows: 1 - :widths: 28 72 - - * - RPC - - Purpose - * - ``SubmitJob`` - - Submit an LP or MILP job in one message (within gRPC message size limits). - * - ``CheckStatus`` - - Poll job status by ``job_id``. - * - ``GetResult`` - - Fetch a completed result (unary, when the payload fits one message). - * - ``DeleteResult`` - - Remove a stored result from server memory. - * - ``CancelJob`` - - Cancel a queued or running job. - * - ``WaitForCompletion`` - - Block until the job finishes (status only; use ``GetResult`` for the solution). - -Chunked Upload (Large Problems) --------------------------------- - -.. list-table:: - :header-rows: 1 - :widths: 28 72 - - * - RPC - - Purpose - * - ``StartChunkedUpload`` - - Begin a session; send problem metadata and settings (arrays follow as chunks). - * - ``SendArrayChunk`` - - Upload one slice of a numeric array field. - * - ``FinishChunkedUpload`` - - Finalize the upload and return ``job_id`` (same as ``SubmitJob``). - -Chunked Download (Large Results) --------------------------------- - -.. list-table:: - :header-rows: 1 - :widths: 28 72 - - * - RPC - - Purpose - * - ``StartChunkedDownload`` - - Begin a download session; returns scalar result fields and array descriptors. - * - ``GetResultChunk`` - - Fetch one chunk of a result array. - * - ``FinishChunkedDownload`` - - End the download session and release server state. - -Streaming and Callbacks ------------------------ - -.. list-table:: - :header-rows: 1 - :widths: 28 72 - - * - RPC - - Purpose - * - ``StreamLogs`` - - Server-streaming solver log lines for a job. - * - ``GetIncumbents`` - - MILP incumbent solutions since a given index. - -Messages and Constraints -======================== - -* **Problem types** — LP and MILP in the enum; the problem payload can include quadratic objective data for **QP**-style solves where the client API supports it. **Routing** over this gRPC service is **not** available yet; it is planned for an **upcoming** release (use REST for remote routing today). -* **Solver settings** — Carried as ``PDLPSolverSettings`` or ``MIPSolverSettings`` inside the request or chunked header, aligned with the NVIDIA cuOpt solver options documentation. -* **Errors** — gRPC status codes carry failures (see comments at the end of ``cuopt_remote_service.proto``). - -Further Reading -=============== - -* :doc:`grpc-server-architecture` — Server process model and job lifecycle (overview); :doc:`advanced` for ``cuopt_grpc_server`` flags. Contributor details: ``cpp/docs/grpc-server-architecture.md``. -* :doc:`advanced` — TLS, Docker, client environment variables, and limitations. diff --git a/docs/cuopt/source/cuopt-grpc/examples.rst b/docs/cuopt/source/cuopt-grpc/examples.rst deleted file mode 100644 index 6da8b76740..0000000000 --- a/docs/cuopt/source/cuopt-grpc/examples.rst +++ /dev/null @@ -1,67 +0,0 @@ -.. - SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - SPDX-License-Identifier: Apache-2.0 - -======== -Examples -======== - -gRPC remote execution uses the same **Python**, **C API**, and **cuopt_cli** entry points as a local solve. After you start ``cuopt_grpc_server`` on the GPU host (:doc:`quick-start`), set the client environment and run **any** of the examples below **unchanged** — no code edits are required. - -On the **client** host, before running the example commands or scripts: - -.. code-block:: bash - - export CUOPT_REMOTE_HOST= - export CUOPT_REMOTE_PORT=5001 - -Add TLS or tuning variables from :doc:`advanced` if your deployment uses them. - -.. note:: - - Routing solve over gRPC is not supported. For solving routing problems remotely today, use the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>` and :doc:`Examples <../cuopt-server/examples/index>`. - -Where to Find Examples -====================== - -Python (LP / QP / MILP) ------------------------ - -* :doc:`../cuopt-python/convex/convex-examples` — runnable Python samples (LP, QP). With ``CUOPT_REMOTE_HOST`` and ``CUOPT_REMOTE_PORT`` set on the client, solves go to the remote server automatically. -* :doc:`../cuopt-python/mip/mip-examples` — runnable Python samples (MILP). With ``CUOPT_REMOTE_HOST`` and ``CUOPT_REMOTE_PORT`` set on the client, solves go to the remote server automatically. - -C API (LP / QP / MILP) ----------------------- - -* :doc:`../cuopt-c/convex/convex-examples` — LP and QP C examples. -* :doc:`../cuopt-c/mip/mip-examples` — MILP C examples. - - Compile and run these programs with the same exports in the shell; ``solve_lp`` / ``solve_mip`` use gRPC when both remote variables are set (see :doc:`../cuopt-c/convex/convex-c-api` for API reference). - -``cuopt_cli`` -------------- - -* :doc:`../cuopt-cli/cli-examples` — ``cuopt_cli`` invocations. With the exports above, the CLI forwards solves to ``cuopt_grpc_server``. - -Minimal Demos (This Section) ----------------------------- - -Bundled with the gRPC docs source for a quick copy-paste path (also walked through in :doc:`quick-start`): - -* :download:`remote_lp_demo.py ` -* :download:`remote_lp_demo.mps ` - -Custom gRPC Client ------------------- - -Integrations that do **not** use the bundled Python / C / CLI stack should speak ``CuOptRemoteService`` directly. See :doc:`api`, :doc:`grpc-server-architecture`, and ``cpp/docs/grpc-server-architecture.md`` in the repository for protos and server behavior. - -More Samples -============ - -* `NVIDIA cuOpt examples on GitHub `_ — set the remote environment on the **client** before running notebooks or scripts. - -REST vs gRPC -============ - -* **Self-hosted HTTP/JSON** — :doc:`../cuopt-server/examples/index` targets the REST server; request shapes follow the OpenAPI workflow, not the ``CuOptRemoteService`` protos. diff --git a/docs/cuopt/source/cuopt-grpc/index.rst b/docs/cuopt/source/cuopt-grpc/index.rst deleted file mode 100644 index 40d84b7e1e..0000000000 --- a/docs/cuopt/source/cuopt-grpc/index.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. - SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - SPDX-License-Identifier: Apache-2.0 - -========================== -gRPC Remote Execution -========================== - -**NVIDIA cuOpt gRPC remote execution** runs optimization solves on a remote GPU host. Clients can be the **Python** API, **C API**, **`cuopt_cli`**, or a **custom** program that speaks ``CuOptRemoteService`` over gRPC. For Python, the C API, and ``cuopt_cli``, set ``CUOPT_REMOTE_HOST`` and ``CUOPT_REMOTE_PORT`` to forward solves to ``cuopt_grpc_server``. - -.. note:: - - **Problem types (gRPC remote):** LP, MILP, and QP are supported today. **Routing** (VRP, TSP, PDP, and related APIs) over gRPC remote execution is **not** available yet; support is planned for an **upcoming** release. For routing against a remote service today, use the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>`. - -This is **not** the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>` (FastAPI). REST is for arbitrary HTTP clients; gRPC is for the bundled remote client in NVIDIA cuOpt's native APIs. - -Start with :doc:`quick-start` (install selector, how remote execution works, Docker, and a minimal LP example). Use :doc:`advanced` for TLS, tuning, limitations, and troubleshooting; :doc:`examples` for additional patterns. - -.. toctree:: - :maxdepth: 2 - :caption: In this section - :name: cuopt-grpc-contents - - quick-start.rst - advanced.rst - examples.rst - api.rst - grpc-server-architecture.md - -See :doc:`../system-requirements` for GPU, CUDA, and OS requirements. diff --git a/docs/cuopt/source/cuopt-grpc/quick-start.rst b/docs/cuopt/source/cuopt-grpc/quick-start.rst deleted file mode 100644 index ae6a1d7b04..0000000000 --- a/docs/cuopt/source/cuopt-grpc/quick-start.rst +++ /dev/null @@ -1,158 +0,0 @@ -.. - SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - SPDX-License-Identifier: Apache-2.0 - -=========== -Quick Start -=========== - -**NVIDIA cuOpt gRPC remote execution** runs LP, MILP, and QP solves on a **GPU host** while your **Python** code, **C API** program, **`cuopt_cli`**, or a **custom** client runs elsewhere. When you set ``CUOPT_REMOTE_HOST`` and ``CUOPT_REMOTE_PORT``, the bundled **Python**, **C API**, and **cuopt_cli** clients forward ``solve_lp`` / ``solve_mip`` to ``cuopt_grpc_server`` with **no code changes**. **Custom** clients call ``CuOptRemoteService`` directly (see :doc:`api`). - -.. note:: - - **Problem types (gRPC remote):** **LP**, **MILP**, and **QP** are supported today. **Routing** (VRP, TSP, PDP) over this path is **not** available; For remote routing, use the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>`. This guide is **not** the REST server—see :doc:`../cuopt-server/index` for HTTP/JSON. - -How Remote Execution Works -========================== - -1. **GPU host** — Run ``cuopt_grpc_server`` (bare metal or in the official container) so it listens on a TCP port (default **5001**). -2. **Client** — Install the NVIDIA cuOpt client libraries on the machine where you invoke the solver. Set ``CUOPT_REMOTE_HOST`` to that GPU host’s address and ``CUOPT_REMOTE_PORT`` to the listen port. -3. **Solve** — Call the same APIs you would for a local solve. The client library opens a gRPC channel, streams the problem, and retrieves the result. Unset the two variables to solve **locally** again (local mode still needs a GPU on that machine where applicable). - -Install NVIDIA cuOpt -==================== - -Use the selector below on the **GPU server** and on **clients** that need Python, the C API, or ``cuopt_cli``. It is pre-set to **C (libcuopt)** because that bundle ships ``cuopt_grpc_server``, ``cuopt_cli``, and libraries together; switch to **Python** if you only need Python packages on a lightweight client. - -.. install-selector:: - :default-iface: c - -Verify the server binary after install: - -.. code-block:: bash - - cuopt_grpc_server --help - -For the same install selector with **Container** / registry choices (Docker Hub or NGC), see :doc:`../install`. - -Run the gRPC Server (GPU Host) -============================== - -**Bare metal** — after activating the same environment you used to install NVIDIA cuOpt: - -.. code-block:: bash - - cuopt_grpc_server --port 5001 --workers 1 - -Leave the process running. Default port **5001**; change ``--port`` if needed and expose the same port on the client side. - -**Docker** — requires `NVIDIA Container Toolkit `_ (or equivalent) on the host. Pull an image tag from :doc:`../install` or the **Container** row in the selector above; substitute ```` below. - -Entrypoint mode (recommended when you are not passing an explicit command): - -.. code-block:: bash - - docker run --gpus all -it --rm -p 5001:5001 \ - -e CUOPT_SERVER_TYPE=grpc \ - - -Or invoke the binary explicitly: - -.. code-block:: bash - - docker run --gpus all -it --rm -p 5001:5001 \ - \ - cuopt_grpc_server --port 5001 --workers 1 - -.. note:: - - The container image defaults to the Python **REST** server when ``CUOPT_SERVER_TYPE`` is unset and you do not override the command; setting ``CUOPT_SERVER_TYPE=grpc`` selects ``cuopt_grpc_server``. Extra environment variables (``CUOPT_SERVER_PORT``, ``CUOPT_GPU_COUNT``, ``CUOPT_GRPC_ARGS``) and TLS are documented in :doc:`Advanced configuration `. - -Point the Client at the Server -============================== - -On the machine where you run Python, the C API, or ``cuopt_cli`` (use ``127.0.0.1`` if the server is on the same host): - -.. code-block:: bash - - export CUOPT_REMOTE_HOST= - export CUOPT_REMOTE_PORT=5001 - -Optional TLS and tuning variables are in :doc:`advanced`. - -Minimal Python Example (LP) -============================ - -The script is the same for **local** or **remote** solves: with the exports above, the client library forwards to ``cuopt_grpc_server``; without them, the solve runs locally (where a GPU is available). -Please make sure the server is running before running the client. - -:download:`remote_lp_demo.py ` - -.. literalinclude:: examples/remote_lp_demo.py - :language: python - :linenos: - -Run the script from your NVIDIA cuOpt Python environment. From a **repository checkout** (repo root): - -.. code-block:: bash - - python docs/cuopt/source/cuopt-grpc/examples/remote_lp_demo.py - -Or, after :download:`downloading ` the file into your current directory: - -.. code-block:: bash - - python remote_lp_demo.py - -You should see an optimal termination. To solve **locally**, unset the remote variables and rerun with the **same** path you used above: - -.. code-block:: bash - - unset CUOPT_REMOTE_HOST CUOPT_REMOTE_PORT - python remote_lp_demo.py - -Minimal ``cuopt_cli`` Example (LP) -================================== - -The same **LP** is available as MPS. With ``CUOPT_REMOTE_HOST`` and ``CUOPT_REMOTE_PORT`` set as above, ``cuopt_cli`` forwards the solve to the remote server; unset them for a **local** run (GPU on that machine). -Please make sure the server is running before running the client. - -:download:`remote_lp_demo.mps ` - -.. literalinclude:: examples/remote_lp_demo.mps - :language: text - -From a **repository checkout** (repo root): - -.. code-block:: bash - - cuopt_cli docs/cuopt/source/cuopt-grpc/examples/remote_lp_demo.mps - -Or, after :download:`downloading ` the MPS into your current directory: - -.. code-block:: bash - - cuopt_cli remote_lp_demo.mps - -To solve **locally** with the same file: - -.. code-block:: bash - - unset CUOPT_REMOTE_HOST CUOPT_REMOTE_PORT - cuopt_cli remote_lp_demo.mps - -More options (time limits, relaxation): :doc:`../cuopt-cli/quick-start` and :doc:`examples`. - -**C API** — With the same environment variables set, call ``solve_lp`` / ``solve_mip`` as in :doc:`../cuopt-c/convex/convex-c-api`. - -More patterns (MPS variants, custom gRPC): :doc:`examples`. - -Next Steps -========== - -* :doc:`../install` — Top-level install selector (all interfaces), including **Container** pulls. -* :doc:`advanced` — TLS / mTLS, Docker environment reference, tuning, limitations, troubleshooting. -* :doc:`examples` — Additional client examples and links to LP/MILP sample collections. -* :doc:`api` and :doc:`grpc-server-architecture` — RPC summary and server behavior overview. - -See :doc:`../system-requirements` for GPU, CUDA, and OS requirements. diff --git a/docs/cuopt/source/cuopt-python/convex/convex-api.rst b/docs/cuopt/source/cuopt-python/convex/convex-api.rst deleted file mode 100644 index bdaae54788..0000000000 --- a/docs/cuopt/source/cuopt-python/convex/convex-api.rst +++ /dev/null @@ -1,46 +0,0 @@ -=================================== -Convex Optimization API Reference -=================================== - -.. autoclass:: cuopt.linear_programming.problem.Problem - :members: - :undoc-members: - :exclude-members: reset_solved_values, populate_solution, dict_to_object, get_incumbent_values, get_pdlp_warm_start_data, getQcsr - -.. autoclass:: cuopt.linear_programming.problem.Variable - :members: - :undoc-members: - :exclude-members: - -.. autoclass:: cuopt.linear_programming.problem.LinearExpression - :members: - :undoc-members: - -.. autoclass:: cuopt.linear_programming.problem.QuadraticExpression - :members: - :undoc-members: - :special-members: __le__, __ge__, __neg__ - -.. autoclass:: cuopt.linear_programming.problem.Constraint - :members: - :undoc-members: - :exclude-members: compute_slack - -.. autoclass:: cuopt.linear_programming.solver_settings.SolverSettings - :members: - :undoc-members: - :exclude-members: to_base_type, toDict - -.. autoclass:: cuopt.linear_programming.problem.CType - :members: - :member-order: bysource - :undoc-members: - :exclude-members: capitalize, casefold, center, count, encode, endswith, expandtabs, find, format, format_map, index, isalnum, isalpha, isascii, isdecimal, isdigit, isidentifier, islower, isnumeric, isprintable, isspace, istitle, isupper, join, ljust, lower, lstrip, maketrans, partition, removeprefix, removesuffix, replace, rfind, rindex, rjust, rpartition, rsplit, rstrip, split, splitlines, startswith, strip, swapcase, title, translate, upper, zfill - -.. autoclass:: cuopt.linear_programming.problem.sense - :members: - :member-order: bysource - :undoc-members: - :exclude-members: __new__, __init__, _generate_next_value_, as_integer_ratio, bit_count, bit_length, conjugate, denominator, from_bytes, imag, is_integer, numerator, real, to_bytes - -.. autofunction:: cuopt.linear_programming.io.Read diff --git a/docs/cuopt/source/cuopt-python/convex/convex-examples.rst b/docs/cuopt/source/cuopt-python/convex/convex-examples.rst deleted file mode 100644 index b8cf50aaa1..0000000000 --- a/docs/cuopt/source/cuopt-python/convex/convex-examples.rst +++ /dev/null @@ -1,265 +0,0 @@ -============================ -Convex Optimization Examples -============================ - -This section contains examples of how to use the cuOpt convex optimization Python API. - -.. note:: - - The examples in this section are not exhaustive. They are provided to help you get started with the cuOpt convex optimization Python API. For more examples, please refer to the `cuopt-examples GitHub repository `_. - - -Simple Linear Programming Example ---------------------------------- - -:download:`simple_lp_example.py ` - -.. literalinclude:: examples/simple_lp_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Optimal solution found in 0.01 seconds - x = 5.0 - y = 5.0 - Objective value = 10.0 - - -.. _simple-qp-example-python: - -Simple Quadratic Programming Example ------------------------------------- - -:download:`simple_qp_example.py ` - -.. literalinclude:: examples/simple_qp_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Optimal solution found in 0.01 seconds - x = 0.5 - y = 0.5 - Objective value = 0.5 - - -.. _simple-socp-example-python: - -Second-Order Cone Programming Example --------------------------------------------- - -:download:`simple_socp_example.py ` - -This example minimizes ``x3`` subject to ``x1 + x2 >= 2`` and the second-order -cone ``||(x1, x2)||_2 <= x3``, expressed as the inequalities -``x1^2 + x2^2 - x3^2 <= 0, x_3 >= 0``. cuOpt detects the cone structure and solves with the -barrier method. - -.. literalinclude:: examples/simple_socp_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Status: 1 - x1 = 1.0 - x2 = 1.0 - x3 = 1.4142135623730951 - Objective value = 1.4142135623730951 - - -.. _rotated-socp-example-python: - -Second-Order Cone Programming with Rotated Second-Order Cones Example ---------------------------------------------------------------------- - -:download:`rotated_socp_example.py ` - -This example solves a **rotated** second-order cone ``x1^2 + x2^2 <= x3 * x4, x3 >= 0, x4 >= 0``. -The cross term is written as ``-x3*x4``. See :doc:`/convex-features` for other RSOC forms. -It minimizes ``x3 + x4`` subject to ``x1 + x2 >= 2``. - -.. literalinclude:: examples/rotated_socp_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Status: 1 - x1 = 1.0 - x2 = 1.0 - x3 = 1.4142135623730951 - x4 = 1.4142135623730951 - Objective value = 2.8284271247461903 - - -.. _general-quadratic-example-python: - -General Convex Quadratic Constraint Example -------------------------------------------- - -:download:`general_quadratic_example.py ` - -This example uses a general convex quadratic constraint -``2*x^2 + 2*x*y + 2*y^2 <= 6`` (an ellipsoid). It minimizes ``x + y``. - -.. literalinclude:: examples/general_quadratic_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Status: 1 - x = -1.0 - y = -1.0 - Objective value = -2.0 - - -.. _mps-example-python: - -Reading a Problem from an MPS File ----------------------------------- - -:download:`mps_example.py `, -:download:`sample.mps ` - -``Problem.read`` loads a problem from an MPS, QPS, or LP file, dispatching on the -file extension. The same call also reads ``QUADOBJ`` quadratic objectives and -and ``QCMATRIX`` quadratic constraints. This example reads the -bundled ``sample.mps`` (a small LP) and solves it. - -.. literalinclude:: examples/mps_example.py - :language: python - :linenos: - -The sample MPS file: - -.. literalinclude:: examples/sample.mps - :language: text - :linenos: - -The response is as follows: - -.. code-block:: text - - Status: 1 - Number of variables: 2 - Objective value = -0.36000000000000004 - VAR1 = 1.8 - VAR2 = 0.0 - - -Advanced Example: Production Planning -------------------------------------- - -:download:`production_planning_example.py ` - -.. literalinclude:: examples/production_planning_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - === Production Planning Solution === - - Status: Optimal - Solve time: 0.09 seconds - Product A production: 36.0 units - Product B production: 28.000000000000004 units - Total profit: $2640.00 - -Working with Expressions and Constraints ----------------------------------------- - -:download:`expressions_constraints_example.py ` - -.. literalinclude:: examples/expressions_constraints_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - === Expression Example Results === - x = 0.0 - y = 50.0 - z = 100.0 - Objective value = 400.0 - -Working with Quadratic Objective Matrix ---------------------------------------- - -:download:`qp_matrix_example.py ` - -.. literalinclude:: examples/qp_matrix_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Optimal solution found in 0.16 seconds - p1 = 30.770728122083014 - p2 = 65.38350784293876 - p3 = 53.84576403497824 - Minimized cost = 1153.8461538953868 - -Inspecting the Problem Solution -------------------------------- - -:download:`solution_example.py ` - -.. literalinclude:: examples/solution_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Optimal solution found in 0.02 seconds - Objective: 9.0 - x = 1.0, ReducedCost = 0.0 - y = 3.0, ReducedCost = 0.0 - z = 0.0, ReducedCost = 2.999999858578644 - c1 DualValue = 1.0000000592359144 - c2 DualValue = 1.0000000821854418 - -Working with PDLP Warmstart Data --------------------------------- - -Warmstart data allows to restart PDLP with a previous solution context. This should be used when you solve a new problem which is similar to the previous one. - -.. note:: - Warmstart data is only available for Linear Programming (LP) problems, not for Mixed Integer Linear Programming (MILP) problems. - -:download:`pdlp_warmstart_example.py ` - -.. literalinclude:: examples/pdlp_warmstart_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Optimal solution found in 0.01 seconds - x = 25.001763214569394 - y = 0.0 - Objective value = 50.00352642913879 diff --git a/docs/cuopt/source/cuopt-python/convex/index.rst b/docs/cuopt/source/cuopt-python/convex/index.rst deleted file mode 100644 index adb48f44f7..0000000000 --- a/docs/cuopt/source/cuopt-python/convex/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -Convex Optimization (LP/QP/QCQP/SOCP) -===================================== - -This section contains details on the cuOpt convex optimization Python API. - -.. toctree:: - :maxdepth: 3 - :caption: LP/QP/QCQP/SOCP Python API - :name: LP/QP/QCQP/SOCP Python API - :titlesonly: - - convex-api.rst - convex-examples.rst diff --git a/docs/cuopt/source/cuopt-python/index.rst b/docs/cuopt/source/cuopt-python/index.rst deleted file mode 100644 index 5b4ea84da6..0000000000 --- a/docs/cuopt/source/cuopt-python/index.rst +++ /dev/null @@ -1,41 +0,0 @@ -======================================== -Python API -======================================== - -NVIDIA cuOpt supports Python API for routing optimization, convex optimization, and mixed-integer programming. - -This section contains details on the cuOpt Python package. - -.. toctree:: - :maxdepth: 3 - :caption: Python API Overview - :name: Python API Overview - :titlesonly: - - quick-start.rst - - -.. toctree:: - :maxdepth: 3 - :caption: Routing Optimization - :name: Routing Optimization - :titlesonly: - - Routing Optimization - - -.. toctree:: - :maxdepth: 3 - :caption: Convex Optimization (LP/QP/QCQP/SOCP) - :name: Convex Optimization Python API - :titlesonly: - - Convex Optimization - -.. toctree:: - :maxdepth: 3 - :caption: Mixed Integer Programming (MIP) - :name: MIP Python API Index - :titlesonly: - - MIP diff --git a/docs/cuopt/source/cuopt-python/mip/index.rst b/docs/cuopt/source/cuopt-python/mip/index.rst deleted file mode 100644 index c67b32b1ae..0000000000 --- a/docs/cuopt/source/cuopt-python/mip/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -MIP (Mixed Integer Programming) -================================ - -.. note:: - - The cuOpt MIP solver is in **beta** and under active development. The solver - currently excels at finding high-quality feasible solutions quickly with - GPU-accelerated primal heuristics. Proving feasible solutions optimal remains - under active development. - -This section contains details on the cuOpt MIP Python API. - -.. toctree:: - :maxdepth: 3 - :caption: MIP Python API - :name: MIP Python API - :titlesonly: - - mip-api.rst - mip-examples.rst diff --git a/docs/cuopt/source/cuopt-python/mip/mip-api.rst b/docs/cuopt/source/cuopt-python/mip/mip-api.rst deleted file mode 100644 index e96f0ef1c1..0000000000 --- a/docs/cuopt/source/cuopt-python/mip/mip-api.rst +++ /dev/null @@ -1,26 +0,0 @@ -================= -MIP API Reference -================= - -MIP uses the same Python API classes as convex optimization — see :doc:`../convex/convex-api` for the full class reference. The following are particularly relevant for MIP problems. - -Variable Types --------------- - -Use :class:`~cuopt.linear_programming.problem.VType` to declare integer and semi-continuous variables: - -.. autoclass:: cuopt.linear_programming.problem.VType - :members: - :member-order: bysource - :undoc-members: - :exclude-members: capitalize, casefold, center, count, encode, endswith, expandtabs, find, format, format_map, index, isalnum, isalpha, isascii, isdecimal, isdigit, isidentifier, islower, isnumeric, isprintable, isspace, istitle, isupper, join, ljust, lower, lstrip, maketrans, partition, removeprefix, removesuffix, replace, rfind, rindex, rjust, rpartition, rsplit, rstrip, split, splitlines, startswith, strip, swapcase, title, translate, upper, zfill - -MIP Start ---------- - -:class:`~cuopt.linear_programming.problem.Problem` supports providing initial feasible solutions (MIP starts) to help the solver find good solutions faster. Use ``Problem.addMIPStart`` to add one or more primal solution hints before calling ``solve()``. - -MIP Solution Callbacks ------------------------ - -:class:`~cuopt.linear_programming.solver_settings.SolverSettings` supports callbacks to monitor incumbent solutions found during the MIP solve. Use ``SolverSettings.set_mip_solution_callback`` to register a callback that receives each new incumbent solution. diff --git a/docs/cuopt/source/cuopt-python/mip/mip-examples.rst b/docs/cuopt/source/cuopt-python/mip/mip-examples.rst deleted file mode 100644 index 2cb9bbd303..0000000000 --- a/docs/cuopt/source/cuopt-python/mip/mip-examples.rst +++ /dev/null @@ -1,78 +0,0 @@ -============ -MIP Examples -============ - -This section contains examples of how to use the cuOpt MIP Python API. - -.. note:: - - The examples in this section are not exhaustive. They are provided to help you get started with the cuOpt mixed integer linear programming Python API. For more examples, please refer to the `cuopt-examples GitHub repository `_. - - -Mixed Integer Linear Programming Example ----------------------------------------- - -:download:`simple_milp_example.py ` - -.. literalinclude:: examples/simple_milp_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Optimal solution found in 0.00 seconds - x = 36.0 - y = 41.0 - Objective value = 303.0 - - -Semi-continuous Variable Example --------------------------------- - -:download:`semi_continuous_example.py ` - -.. literalinclude:: examples/semi_continuous_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Optimal solution found in 0.00 seconds - x = 0.0 - y = 1.0 - Objective value = 0.0 - - -Working with Incumbent Solutions --------------------------------- - -Incumbent solutions are intermediate feasible solutions found during the MIP solving process. They represent the best integer-feasible solution discovered so far and can be accessed through callback functions. - -.. note:: - Incumbent solutions are only available for Mixed Integer Programming (MIP) problems, not for pure Linear Programming (LP) problems. - -:download:`incumbent_solutions_example.py ` - -.. literalinclude:: examples/incumbent_solutions_example.py - :language: python - :linenos: - -The response is as follows: - -.. code-block:: text - - Optimal solution found. - Incumbent 1: x=36.0 y=41.0 cost: 303.00 - Solution objective: 303.000000 , relative_mip_gap 0.000000 solution_bound 303.000000 presolve_time 0.103659 total_solve_time 0.173678 max constraint violation 0.000000 max int violation 0.000000 max var bounds violation 0.000000 nodes 0 simplex_iterations 2 - - === Final Results === - Problem status: Optimal - Solve time: 0.17 seconds - Final solution: x=36.0 y=41.0 - Final objective value: 303.00 - - Total incumbent solutions found: 1 diff --git a/docs/cuopt/source/cuopt-python/quick-start.rst b/docs/cuopt/source/cuopt-python/quick-start.rst deleted file mode 100644 index fde1f3d29a..0000000000 --- a/docs/cuopt/source/cuopt-python/quick-start.rst +++ /dev/null @@ -1,48 +0,0 @@ -================= -Quickstart Guide -================= - -NVIDIA cuOpt provides a Python API for routing optimization and LP/QP/MILP that enables users to solve complex optimization problems efficiently. - -Installation -============ - -Choose your install method below; the selector is pre-set for the Python API. Copy the command and run it in your environment. See :doc:`../install` for all interfaces and options. - -.. install-selector:: - :default-iface: python - -NVIDIA Launchable -------------------- - -NVIDIA cuOpt can be tested with `NVIDIA Launchable `_ with `example notebooks `_. For more details, please refer to the `NVIDIA Launchable documentation `_. - -Smoke Test ----------- - -After installation, you can verify that NVIDIA cuOpt is working correctly by running a simple test. - -Copy and paste this script directly into your terminal (:download:`smoke_test_example.sh `): - -.. literalinclude:: routing/examples/smoke_test_example.sh - :language: bash - :linenos: - :start-after: # Users can copy - - -Example Response: - -.. code-block:: text - - route arrival_stamp truck_id location type - 0 0.0 0 0 Depot - 2 2.0 0 2 Delivery - 1 4.0 0 1 Delivery - 0 6.0 0 0 Depot - - - ****************** Display Routes ************************* - Vehicle-0 starts at: 0.0, completes at: 6.0, travel time: 6.0, Route : - 0(Dpt)->2(D)->1(D)->0(Dpt) - - This results in a travel time of 6.0 to deliver all routes diff --git a/docs/cuopt/source/cuopt-python/routing/index.rst b/docs/cuopt/source/cuopt-python/routing/index.rst deleted file mode 100644 index 7fb7329db3..0000000000 --- a/docs/cuopt/source/cuopt-python/routing/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -======================================== -Routing Optimization -======================================== - -This section contains details on the cuOpt routing optimization Python API. - -.. toctree:: - :maxdepth: 3 - :caption: Routing - :name: Routing - :titlesonly: - - routing-api.rst - routing-examples.rst diff --git a/docs/cuopt/source/cuopt-python/routing/routing-api.rst b/docs/cuopt/source/cuopt-python/routing/routing-api.rst deleted file mode 100644 index 8ea5acae4c..0000000000 --- a/docs/cuopt/source/cuopt-python/routing/routing-api.rst +++ /dev/null @@ -1,32 +0,0 @@ -======================================== -cuOpt Routing Python API Reference -======================================== - -.. automodule:: cuopt.distance_engine - :members: - :undoc-members: - :show-inheritance: - :imported-members: - -.. autoclass:: cuopt.routing.SolutionStatus - :members: - :undoc-members: - :show-inheritance: - -.. autoclass:: cuopt.routing.DataModel - :members: - :undoc-members: - -.. autoclass:: cuopt.routing.SolverSettings - :members: - :undoc-members: - :show-inheritance: - -.. autofunction:: cuopt.routing.Solve - -.. autofunction:: cuopt.routing.BatchSolve - -.. autoclass:: cuopt.routing.Assignment - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/cuopt/source/cuopt-python/routing/routing-examples.rst b/docs/cuopt/source/cuopt-python/routing/routing-examples.rst deleted file mode 100644 index 3bc5400c4b..0000000000 --- a/docs/cuopt/source/cuopt-python/routing/routing-examples.rst +++ /dev/null @@ -1,68 +0,0 @@ -======================================== -Routing Examples -======================================== - -This section contains examples for the cuOpt routing Python API. - -Intra-factory Transport ------------------------ - -A capacitated pickup-and-delivery problem with time windows (PDPTW) for a fleet -of autonomous mobile robots (AMRs) moving parts between processing stations on a -factory floor. The example uses :class:`cuopt.distance_engine.WaypointMatrix` to -derive a cost matrix from a weighted waypoint graph, sets up pickup/delivery -orders with demand and time windows, solves with :func:`cuopt.routing.Solve`, -and expands the target-location route back to a waypoint-level route per robot. - -.. image:: images/waypoint_graph.png - :alt: Waypoint graph - -**Problem details:** - -- 4 target locations: 1 start location for AMRs and 3 processing stations -- 6 transport orders (pickup/delivery pairs) with individual time windows -- 2 AMRs, each with a carrying capacity of 2 parts -- Factory hours: 0 to 100 time units - -:download:`intra_factory_example.py ` - -.. literalinclude:: examples/intra_factory_example.py - :language: python - :linenos: - -TSP Batch Mode --------------- - -The routing Python API supports **batch mode** for solving many TSP (or routing) instances in a single call. Instead of calling :func:`cuopt.routing.Solve` repeatedly, you build a list of :class:`cuopt.routing.DataModel` objects and call :func:`cuopt.routing.BatchSolve`. The solver runs the problems in parallel to improve throughput. - -**When to use batch mode:** - -- You have **many similar routing problems** (e.g., dozens or hundreds of small TSPs). -- You want to **maximize throughput** by utilizing the GPU across multiple problems at once. -- Problem sizes and structure are compatible with the same :class:`cuopt.routing.SolverSettings` (e.g., same time limit). - -**Returns:** A list of :class:`cuopt.routing.Assignment` objects, one per input data model, in the same order as ``data_model_list``. Use :meth:`cuopt.routing.Assignment.get_status` and other assignment methods to inspect each solution. - -The following example builds several TSPs of different sizes, solves them in one batch, and prints a short summary per solution. - -:download:`tsp_batch_example.py ` - -.. literalinclude:: examples/tsp_batch_example.py - :language: python - :linenos: - -Sample output: - -.. code-block:: text - - Solved 6 TSPs in batch. - TSP 0 (size 5): status=SUCCESS, vehicles=1 - TSP 1 (size 8): status=SUCCESS, vehicles=1 - TSP 2 (size 10): status=SUCCESS, vehicles=1 - ... - -**Notes:** - -- All problems in the batch use the **same** :class:`cuopt.routing.SolverSettings` (e.g., time limit, solver options). -- Callbacks are not supported in batch mode. -- For best practices when batching many instances, see the *Add best practices for batch solving* note in the release documentation. diff --git a/docs/cuopt/source/cuopt-server/client-api/index.rst b/docs/cuopt/source/cuopt-server/client-api/index.rst deleted file mode 100644 index 4cd44eba23..0000000000 --- a/docs/cuopt/source/cuopt-server/client-api/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -======================================== -Client API Overview -======================================== - -Client is used to communicate with the cuOpt server. More details can be found below. - -.. toctree:: - :maxdepth: 3 - :caption: Client API Overview - :name: Client API Overview - :titlesonly: - - sh-cli-api.rst - sh-cli-build.rst diff --git a/docs/cuopt/source/cuopt-server/client-api/sh-cli-api.rst b/docs/cuopt/source/cuopt-server/client-api/sh-cli-api.rst deleted file mode 100644 index a681fe180b..0000000000 --- a/docs/cuopt/source/cuopt-server/client-api/sh-cli-api.rst +++ /dev/null @@ -1,47 +0,0 @@ -========================================= -Self-Hosted Service Client API Reference -========================================= - -Client ---------- - -Service Client -############### - -.. autoclass:: cuopt_sh_client.CuOptServiceSelfHostClient - :members: - :undoc-members: - -LP Supporting Classes ---------------------- - -.. autoclass:: cuopt_sh_client.PDLPSolverMode - :members: - :undoc-members: - :no-inherited-members: - -.. autoclass:: cuopt_sh_client.SolverMethod - :members: - :undoc-members: - :no-inherited-members: - -.. autoclass:: cuopt.linear_programming.data_model.DataModel - :members: - :undoc-members: - -.. autoclass:: cuopt_sh_client.ThinClientSolverSettings - :members: - :undoc-members: - -.. autoclass:: cuopt_sh_client.ThinClientSolution - :members: - :undoc-members: - -Self-Hosted Client CLI ----------------------- - -CLI provides an option for users to interact with the cuOpt on C level using mps files as input. - -.. literalinclude:: sh-cli-help.txt - :language: shell - :linenos: diff --git a/docs/cuopt/source/cuopt-server/client-api/sh-cli-build.rst b/docs/cuopt/source/cuopt-server/client-api/sh-cli-build.rst deleted file mode 100644 index 091bd54188..0000000000 --- a/docs/cuopt/source/cuopt-server/client-api/sh-cli-build.rst +++ /dev/null @@ -1,113 +0,0 @@ -====================================== -Build Your Own Self-Hosted Thin Client -====================================== - - -Overview ----------------- - -The thin client and CLI provide ease of access to the cuOpt service, but these are just references on how to communicate with the cuOpt service. Underlying details of the API have been discussed below if you want to create your own thin client. - -.. note:: - Please don't use ``cuopt_self_host_client.py`` or ``cuopt_sh_client.py`` as your thin client name, just to avoid confusion in case if you had installed cuopt thin client from pypi. - -Invoking cuOpt Service -------------------------- - -Sending Request -############### - -The ``requestBody`` contains a JSON object which will be processed by cuOpt. ``/cuopt/request`` endpoint is used to send the request. - -Run server with following command in a terminal: - -Here we are sharing current directory as both data and result directory. - -.. code-block:: bash - :linenos: - - ip="0.0.0.0" - port=5000 - python -m cuopt_server.cuopt_service -d="$PWD" -r="$PWD" -i="$ip" -p="$port" - -Example with JSON data as a direct string: - -.. code-block:: bash - :linenos: - - ip="0.0.0.0" - port=5000 - - curl --location "http://$ip:$port/cuopt/request" --header 'Content-Type: application/json' -d '{ - "cost_matrix_data": {"data": {"0": [[0, 1], [1, 0]]}}, - "task_data": {"task_locations": [1], "demand": [[1]], "task_time_windows": [[0, 10]], "service_times": [1]}, - "fleet_data": {"vehicle_locations":[[0, 0]], "capacities": [[2]], "vehicle_time_windows":[[0, 20]] }, - "solver_config": {"time_limit": 2} - }' - -Example with JSON data as a file: - -.. code-block:: bash - :linenos: - - ip="0.0.0.0" - port=5000 - - echo '{ - "cost_matrix_data": {"data": {"0": [[0, 1], [1, 0]]}}, - "task_data": {"task_locations": [0, 1]}, - "fleet_data": {"vehicle_locations": [[0, 0], [0, 0]]}, - "solver_config": {"time_limit": 2} - }' > data.json - - curl --location "http://$ip:$port/cuopt/request" \ - --header 'Content-Type: application/json' \ - --header 'CUOPT-DATA-FILE: data.json' \ - -d '{}' - - -Success Response: - -.. code-block:: json - - {"reqId":"1df28c33-8b8c-4bb7-9ff9-1e19929094c6"} - - -When sending files to the server, the server must be configured with appropriate data and result directories to temporarily store these files. These directories can be set using the ``-d`` and ``-r`` options when starting the server. Please refer to the :doc:`Server CLI documentation <../server-api/server-cli>` for more details on configuring these directories. - -``JSON_DATA`` should follow the :doc:`spec under "POST /cuopt/request" schema <../../open-api>` described for cuOpt input. - -Polling for Request Status: ---------------------------- - -The cuOpt service employs an asynchronous interface for invocation and result retrieval. -When you make an invocation request, the system will submit your problem to the solver and return a request id. - -Users can poll the request id for status with the help of ``/cuopt/request/{request-id}`` endpoint. - -.. code-block:: bash - :linenos: - - curl --location "http://$ip:$port/cuopt/request/{request-id}" - -In case the solver has completed the job, the response will be "completed". - -Please refer to the :doc:`Solver status in spec using "GET /cuopt/request/{request-id}" <../../open-api>` for more details on responses. - - -cuOpt Result Retrieval ------------------------- - -Once you have received successful response from solver with status "completed", you can retrieve the result with the help of ``/cuopt/solution/{request-id}`` endpoint. - -.. code-block:: bash - :linenos: - - curl --location "http://$ip:$port/cuopt/solution/{request-id}" - - -This would fetch the result in JSON format. Please refer to the :doc:`Response structure in spec using "GET /cuopt/solution/{request-id}" <../../open-api>` for more details on responses. - - -.. important:: - It is user's responsibility to delete the request and solution files from the data and result directories respectively after retrieving the result. Please refer to the API spec for more details on deletion. diff --git a/docs/cuopt/source/cuopt-server/csp-guides/csp-aws.rst b/docs/cuopt/source/cuopt-server/csp-guides/csp-aws.rst deleted file mode 100644 index 8e80d4c5d0..0000000000 --- a/docs/cuopt/source/cuopt-server/csp-guides/csp-aws.rst +++ /dev/null @@ -1,54 +0,0 @@ -====================== -AWS Quickstart Guide -====================== - -By the end of this guide, you will have registered for NVIDIA AI Enterprise, created a Virtual Machine (VM) on Amazon Web Services (AWS) that uses the NVIDIA AI Enterprise image, and run a sample cuOpt workload. This will prepare you to run cuOpt on an AWS VM. - - -Step 1: Create an AWS VM with NVAIE Image --------------------------------------------- - -#. Log in to AWS and navigate to AWS Marketplace. - -#. Look up NVIDIA AI Enterprise in the search bar. If you already have a subscription, NVIDIA AI Enterprise may automatically show in your subscriptions. Otherwise, search for **NVIDIA AI Enterprise** in the search bar. - - .. image:: images/aws1.png - :width: 9.5in - :height: 5in - -#. Click on the **NVIDIA AI Enterprise** subscription. -#. The following page will open. Click **Launch new instance**. - - .. image:: images/aws2.png - :width: 9.5in - :height: 5in - -#. Configure your VM. If you have deployed a VM through AWS in the past, you should already have an SSH key you can reuse. If this is your first time, create a new SSH key. - - .. note:: - When selecting the hardware configuration, make sure to select a GPU of A100 or higher. - - .. image:: images/aws3.png - :width: 9.5in - :height: 5in - -#. After configuring your VM, wait for it to deploy. This might take a few minutes to complete. - -#. Upon deployment completion, you will see the following screen. Select **Connect to your instance**. - - .. image:: images/aws4.png - :width: 9.5in - :height: 5in - -#. Once you have the IP address of your VM, you can connect to it through the AWS SSH portal, or through your terminal. - - -Step 2: Activate NVAIE Subscription ------------------------------------- - -Once connected to the VM, generate an identity token. Activate your NVIDIA AI Enterprise subscription using that identity token on NGC. Follow the instructions `here `__. - -Step 3: Run cuOpt ------------------- - -To run cuOpt, you will need to log in to the NVIDIA Container Registry, pull the cuOpt container, and then run it. To test that it is successfully running, you can run a sample cuOpt request. This process is the same for deploying cuOpt on your own infrastructure. Refer to :ref:`Self-Hosted Service Quickstart Guide `. diff --git a/docs/cuopt/source/cuopt-server/csp-guides/csp-azure.rst b/docs/cuopt/source/cuopt-server/csp-guides/csp-azure.rst deleted file mode 100644 index af27be0ba7..0000000000 --- a/docs/cuopt/source/cuopt-server/csp-guides/csp-azure.rst +++ /dev/null @@ -1,74 +0,0 @@ -====================== -Azure Quickstart Guide -====================== - -By the end of this guide, you will have registered for NVIDIA AI Enterprise, created a Virtual Machine (VM) on Microsoft Azure that uses the NVIDIA AI Enterprise image, run a sample cuOpt workload, and you will be ready to run an instance of cuOpt on an Azure VM. -To complement this guide, you can watch the video tutorial `here `__. The video tutorial and this user guide cover the same deployment steps. - -Step 1: Create an Azure VM with NVAIE Image --------------------------------------------- - -#. Log in to Azure and navigate to Azure Marketplace. - -#. Look up **NVIDIA AI Enterprise** in the search bar. - - .. image:: images/azure1.png - :width: 9.5in - :height: 5in - -#. You should see the following results. Click on the first option, the **NVIDIA AI Enterprise** Virtual Machine. - - .. image:: images/azure2.png - :width: 9.5in - :height: 5in - -#. The following page will open. Click **Create**. - - .. image:: images/azure3.png - :width: 9.5in - :height: 5in - -#. Configure your VM. If you have deployed a VM through Azure in the past, you should already have an SSH key you can reuse. If this is your first time, create a new SSH key. - - .. note:: - When selecting the hardware configuration, make sure to select a GPU of A100 or higher. - -#. After configuring your VM, wait for it to deploy. This might take a few minutes to complete. - -#. Upon deployment completion, you will see the following screen. Select **Go to resource**. - - .. image:: images/azure4.png - :width: 9.5in - :height: 5in - -#. This will take you to the Overview page of your VM. Select **Connect** at the top of the page. - - .. image:: images/azure5.png - :width: 9.5in - :height: 5in - -#. You can connect to the VM through Azure CLI or through your terminal (Native SSH). The public IP address of your VM is written at the top of the page. - - .. image:: images/azure6.png - :width: 9.5in - :height: 5in - -Step 2: Activate NVAIE Subscription ------------------------------------- - -Once connected to the VM, generate an identity token. Activate your NVIDIA AI Enterprise subscription using that identity token on NGC. Follow the instructions `here `__. - -Step 3: Run cuOpt ------------------- - -To run cuOpt, you will need to log in to the NVIDIA Container Registry, pull the cuOpt container, and then run it. To test that it is successfully running, you can run a sample cuOpt request. This process is the same for deploying cuOpt on your own infrastructure. Refer :ref:`Self-Hosted Service Quickstart Guide `. - - -Step 4: Mapping Visualization with Azure ------------------------------------------ - -Upon running your optimization problems and getting the optimized routes from cuOpt, you may wish to post-process your data and visualize your data on a map. One tool you can use for this is Azure Maps. Linked below are a demo application and sample code you can use as a reference. - -- `Demo application `__ - -- `Sample code `__ diff --git a/docs/cuopt/source/cuopt-server/csp-guides/index.rst b/docs/cuopt/source/cuopt-server/csp-guides/index.rst deleted file mode 100644 index 12dd0e0e27..0000000000 --- a/docs/cuopt/source/cuopt-server/csp-guides/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -======================================== -CSP Guides -======================================== - -This section contains guides on how to use the cuOpt server in different cloud providers. - -.. toctree:: - :maxdepth: 3 - :caption: CSP Guides Overview - :name: CSP Guides Overview - :titlesonly: - - csp-azure.rst - csp-aws.rst diff --git a/docs/cuopt/source/cuopt-server/examples/index.rst b/docs/cuopt/source/cuopt-server/examples/index.rst deleted file mode 100644 index e27f858309..0000000000 --- a/docs/cuopt/source/cuopt-server/examples/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -======================================== -Examples -======================================== - -This section contains examples on how to use the cuOpt server using the client. - -.. toctree:: - :maxdepth: 3 - :caption: Examples Overview - :name: Examples Overview - :titlesonly: - - lp-examples.rst - milp-examples.rst - routing-examples.rst diff --git a/docs/cuopt/source/cuopt-server/examples/lp-examples.rst b/docs/cuopt/source/cuopt-server/examples/lp-examples.rst deleted file mode 100644 index 2b32b88fde..0000000000 --- a/docs/cuopt/source/cuopt-server/examples/lp-examples.rst +++ /dev/null @@ -1,461 +0,0 @@ -=============================== -LP Python Examples -=============================== - -The following example showcases how to use the ``CuOptServiceSelfHostClient`` to solve a simple LP problem in normal mode and batch mode (where multiple problems are solved at once). - -.. note:: - LP batch mode is deprecated and will be removed in a future release. Prefer - sequential ``cuopt.linear_programming.Solve`` calls. Server and CLI batch - paths still use batch solve today; check response ``warnings`` or logs for the - deprecation notice. - -The OpenAPI specification for the server is available in :doc:`open-api spec <../../open-api>`. The example data is structured as per the OpenAPI specification for the server, please refer :doc:`LPData under "POST /cuopt/request" <../../open-api>` under schema section. LP and MILP share same spec. - -If you want to run server locally, please run the following command in a terminal or tmux session so you can test examples in another terminal. - -.. code-block:: bash - :linenos: - - export ip="localhost" - export port=5000 - python -m cuopt_server.cuopt_service --ip $ip --port $port - -.. _generic-example-with-normal-and-batch-mode: - -Genric Example With Normal Mode and Batch Mode ------------------------------------------------- - -:download:`basic_lp_example.py ` - -.. literalinclude:: lp/examples/basic_lp_example.py - :language: python - :linenos: - - -The response would be as follows: - -Normal mode response: - -.. code-block:: json - :linenos: - - { - "response": { - "solver_response": { - "status": "Optimal", - "solution": { - "problem_category": "LP", - "primal_solution": [ - 1.8, - 0.0 - ], - "dual_solution": [ - -0.06666666666666668, - 0.0 - ], - "primal_objective": -0.36000000000000004, - "dual_objective": 6.92188481708744e-310, - "solver_time": 0.006462812423706055, - "vars": {}, - "lp_statistics": { - "primal_residual": 6.92114652678267e-310, - "dual_residual": 6.9218848170975e-310, - "gap": 6.92114652686054e-310, - "nb_iterations": 1 - }, - "reduced_cost": [ - 0.0, - 0.0031070813207920247 - ], - "milp_statistics": {} - } - }, - "total_solve_time": 0.013341188430786133 - }, - "reqId": "c7f2e5a1-d210-4e2e-9308-4257d0a86c4a" - } - - - -Batch mode response: - -.. code-block:: json - :linenos: - - { - "response": { - "solver_response": [ - { - "status": "Optimal", - "solution": { - "problem_category": "LP", - "primal_solution": [ - 1.8, - 0.0 - ], - "dual_solution": [ - -0.06666666666666668, - 0.0 - ], - "primal_objective": -0.36000000000000004, - "dual_objective": 6.92188481708744e-310, - "solver_time": 0.005717039108276367, - "vars": {}, - "lp_statistics": { - "primal_residual": 6.92114652678267e-310, - "dual_residual": 6.9218848170975e-310, - "gap": 6.92114652686054e-310, - "nb_iterations": 1 - }, - "reduced_cost": [ - 0.0, - 0.0031070813207920247 - ], - "milp_statistics": {} - } - }, - { - "status": "Optimal", - "solution": { - "problem_category": "LP", - "primal_solution": [ - 1.8, - 0.0 - ], - "dual_solution": [ - -0.06666666666666668, - 0.0 - ], - "primal_objective": -0.36000000000000004, - "dual_objective": 6.92188481708744e-310, - "solver_time": 0.007481813430786133, - "vars": {}, - "lp_statistics": { - "primal_residual": 6.921146112128e-310, - "dual_residual": 6.9218848170975e-310, - "gap": 6.92114611220587e-310, - "nb_iterations": 1 - }, - "reduced_cost": [ - 0.0, - 0.0031070813207920247 - ], - "milp_statistics": {} - } - } - ], - "total_solve_time": 0.013 - }, - "reqId": "69dc8f36-16c3-4e28-8fb9-3977eb92b480" - } - -.. note:: - Warm start is only applicable to LP and not for MILP. - -.. _warm-start: - -Warm Start ----------- - -Previously run solutions can be saved and be used as warm start for new requests using previously run reqIds as follows: - -:download:`warmstart_example.py ` - -.. literalinclude:: lp/examples/warmstart_example.py - :language: python - :linenos: - -The response would be as follows: - -.. code-block:: json - :linenos: - - { - "response": { - "solver_response": { - "status": "Optimal", - "solution": { - "problem_category": "LP", - "primal_solution": [ - 1.8, - 0.0 - ], - "dual_solution": [ - -0.06666666666666668, - 0.0 - ], - "primal_objective": -0.36000000000000004, - "dual_objective": 6.92188481708744e-310, - "solver_time": 0.006613016128540039, - "vars": {}, - "lp_statistics": { - "primal_residual": 6.921146112128e-310, - "dual_residual": 6.9218848170975e-310, - "gap": 6.92114611220587e-310, - "nb_iterations": 1 - }, - "reduced_cost": [ - 0.0, - 0.0031070813207920247 - ], - "milp_statistics": {} - } - }, - "total_solve_time": 0.013310909271240234 - }, - "reqId": "6d1e278f-5505-4bcc-8a33-2f7f7d6f8a30" - } - - -Using MPS or LP file directly ------------------------------ - -The self-hosted client accepts both MPS and LP format files — the client -dispatches on the file extension (``.lp`` ⇒ LP parser, otherwise MPS) and -sends the parsed data model to the server. An example on using .mps files -as input is shown below: - -:download:`mps_file_example.py ` - -.. literalinclude:: lp/examples/mps_file_example.py - :language: python - :linenos: - -The response is: - -.. code-block:: json - :linenos: - - { - "response": { - "solver_response": { - "status": "Optimal", - "solution": { - "problem_category": "LP", - "primal_solution": [ - 1.8, - 0.0 - ], - "dual_solution": [ - -0.06666666666666668, - 0.0 - ], - "primal_objective": -0.36000000000000004, - "dual_objective": 6.92188481708744e-310, - "solver_time": 0.008397102355957031, - "vars": { - "VAR1": 1.8, - "VAR2": 0.0 - }, - "lp_statistics": { - "primal_residual": 6.921146112128e-310, - "dual_residual": 6.9218848170975e-310, - "gap": 6.92114611220587e-310, - "nb_iterations": 1 - }, - "reduced_cost": [ - 0.0, - 0.0031070813207920247 - ], - "milp_statistics": {} - } - }, - "total_solve_time": 0.014980316162109375 - }, - "reqId": "3f36bad7-6135-4ffd-915b-858c449c7cbb" - } - - -Generate Datamodel using Problem File Parser --------------------------------------------- - -Use a :class:`~cuopt.linear_programming.data_model.DataModel` built with -:func:`~cuopt.linear_programming.io.Read` as input to ``get_LP_solve``; -the client dispatches on the file extension (``.mps`` / ``.qps`` vs ``.lp``, -including ``.gz`` / ``.bz2`` compressed variants). For solver settings see -:doc:`convex optimization parameters <../../convex-settings>` and :doc:`MIP parameters <../../mip-settings>`. - -MPS format -~~~~~~~~~~ - -:download:`mps_datamodel_example.py ` - -.. literalinclude:: lp/examples/mps_datamodel_example.py - :language: python - :linenos: - -LP format -~~~~~~~~~ - -:download:`lp_datamodel_example.py ` - -.. literalinclude:: lp/examples/lp_datamodel_example.py - :language: python - :linenos: - -Expected output (either example, same problem instance) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: text - :linenos: - - Termination Reason: (1 is Optimal) - 1 - Objective Value: - -0.36000000000000004 - MPS Parse time: 0.000 sec - Network time: 1.062 sec - Engine Solve time: 0.004 sec - Total end to end time: 1.066 sec - Variables Values: - {'VAR1': 1.8, 'VAR2': 0.0} - -Example with DataModel is available in the `Examples Notebooks Repository `_. - -The ``data`` argument to ``get_LP_solve`` may be a dictionary of the format shown in :doc:`LP Open-API spec <../../open-api>`. More details on the response can be found under the responses schema :doc:`"get /cuopt/request" and "get /cuopt/solution" API spec <../../open-api>`. - - -Aborting a Running Job in Thin Client -------------------------------------- - -Please refer to the :ref:`aborting-thin-client` in the MILP Example for more details. - - -================================================= -LP CLI Examples -================================================= - -Generic Example ---------------- - -The following examples showcase how to use the ``cuopt_sh`` CLI to solve a simple LP problem. - -.. code-block:: shell - - echo '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [3.0, 4.0, 2.7, 10.1] - }, - "constraint_bounds": { - "upper_bounds": [5.4, 4.9], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [0.2, 0.1], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": "False", - "solver_config": { - "tolerances": { - "optimality": 0.0001 - } - } - }' > data.json - -Invoke the CLI. - -.. code-block:: shell - - # Please update these values if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - cuopt_sh data.json -t LP -i $ip -p $port -sl - -Response is as follows: - -.. code-block:: json - :linenos: - - { - "response": { - "solver_response": { - "status": "Optimal", - "solution": { - "problem_category": "LP", - "primal_solution": [1.8, 0.0], - "dual_solution": [-0.06666666666666668, 0.0], - "primal_objective": -0.36000000000000004, - "dual_objective": 6.92188481708744e-310, - "solver_time": 0.007324934005737305, - "vars": {}, - "lp_statistics": { - "primal_residual": 6.921146112128e-310, - "dual_residual": 6.9218848170975e-310, - "gap": 6.92114611220587e-310, - "nb_iterations": 1 - }, - "reduced_cost": [0.0, 0.0031070813207920247], - "milp_statistics": {} - } - }, - "total_solve_time": 0.014164209365844727 - }, - "reqId": "4665e513-341e-483b-85eb-bced04ba598c" - } - -Warm Start in CLI ------------------ - -To use a previous solution as the initial/warm start solution for a new request ID, you are required to save the previous solution, which can be accomplished use option ``-k``. Use the previous reqId in the next request as follows: - -.. note:: - Warm start is only applicable to LP and not for MILP. - -.. code-block:: shell - - # Please update these values if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - reqId=$(cuopt_sh -t LP data.json -i $ip -p $port -k | sed "s/'/\"/g" | sed 's/False/false/g' | jq -r '.reqId') - - cuopt_sh data.json -t LP -i $ip -p $port -wid $reqId - -In case the user needs to update solver settings through CLI, the option ``-ss`` can be used as follows: - -.. code-block:: shell - - # Please update these values if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - cuopt_sh data.json -t LP -i $ip -p $port -ss '{"tolerances": {"optimality": 0.0001}, "time_limit": 5}' - -In the case of batch mode, you can send a bunch of ``mps`` files at once, and acquire results. The batch mode works only for ``mps`` in the case of CLI: - -.. note:: - LP batch mode is deprecated; see :ref:`Batch Mode ` in - :doc:`../../convex-features`. - -.. note:: - Batch mode is not available for MILP problems. - -A sample MPS file (:download:`sample.mps `): - -.. literalinclude:: lp/examples/sample.mps - :language: text - :linenos: - -Run the example: - -.. code-block:: bash - - # Please update these values if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - cuopt_sh sample.mps sample.mps sample.mps -t LP -i $ip -p $port -ss '{"tolerances": {"optimality": 0.0001}, "time_limit": 5}' - - -Aborting a Running Job In CLI ------------------------------ - -Please refer to the :ref:`aborting-cli` in the MILP Example for more details. - -.. note:: - Please use solver settings while using .mps files. diff --git a/docs/cuopt/source/cuopt-server/examples/milp-examples.rst b/docs/cuopt/source/cuopt-server/examples/milp-examples.rst deleted file mode 100644 index 9bb677b3a0..0000000000 --- a/docs/cuopt/source/cuopt-server/examples/milp-examples.rst +++ /dev/null @@ -1,227 +0,0 @@ -======================================== -MILP Python Examples -======================================== - -The major difference between this example and the LP example is that some of the variables are integers, so ``variable_types`` need to be shared. -The OpenAPI specification for the server is available in :doc:`open-api spec <../../open-api>`. The example data is structured as per the OpenAPI specification for the server, please refer :doc:`LPData data under "POST /cuopt/request" <../../open-api>` under schema section. LP and MILP share same spec. - -Generic Example ---------------- - -:download:`basic_milp_example.py ` - -.. literalinclude:: milp/examples/basic_milp_example.py - :language: python - :linenos: - -The response would be as follows: - -.. code-block:: json - :linenos: - - { - "response": { - "solver_response": { - "status": "Optimal", - "solution": { - "problem_category": "MIP", - "primal_solution": [ - 0.0, - 5000.0 - ], - "dual_solution": null, - "primal_objective": 8500.0, - "dual_objective": null, - "solver_time": 0.0, - "vars": { - "x": 0.0, - "y": 5000.0 - }, - "lp_statistics": {}, - "reduced_cost": null, - "milp_statistics": { - "mip_gap": 0.0, - "solution_bound": 8500.0, - "presolve_time": 0.007354775, - "max_constraint_violation": 0.0, - "max_int_violation": 0.0, - "max_variable_bound_violation": 0.0, - "num_nodes": 1999468624, - "num_simplex_iterations": 21951 - } - } - }, - "total_solve_time": 0.08600544929504395 - }, - "reqId": "524e2e37-3494-4c16-bd06-2a9bfd768f76" - } - -.. _incumbent-and-logging-callback: - -Incumbent and Logging Callback ------------------------------- - -The incumbent solution can be retrieved using a callback function as follows: - -.. note:: - Incumbent solution callback is only applicable to MILP. - The callback bound can be ``None`` when the solver has found an incumbent - but no finite global bound is available yet. - -:download:`incumbent_callback_example.py ` - -.. literalinclude:: milp/examples/incumbent_callback_example.py - :language: python - :linenos: - -Log the callback response: - -.. code-block:: text - :linenos: - - server-log: Solving a problem with 1 constraints 2 variables (2 integers) and 2 nonzeros - server-log: Objective offset 0.000000 scaling_factor -1.000000 - server-log: After trivial presolve updated 1 constraints 2 variables - server-log: Running presolve! - server-log: Solving LP root relaxation - ..... - -Incumbent callback response: - -.. code-block:: text - :linenos: - - Solution : [0.0, 5000.0] cost : 8500.0 - -.. code-block:: json - :linenos: - - { - "response": { - "solver_response": { - "status": "Optimal", - "solution": { - "problem_category": "MIP", - "primal_solution": [ - 0.0, - 5000.0 - ], - "dual_solution": null, - "primal_objective": 8500.0, - "dual_objective": null, - "solver_time": 0.0, - "vars": { - "x": 0.0, - "y": 5000.0 - }, - "lp_statistics": {}, - "reduced_cost": null, - "milp_statistics": { - "mip_gap": 0.0, - "solution_bound": 8500.0, - "presolve_time": 0.001391178, - "max_constraint_violation": 0.0, - "max_int_violation": 0.0, - "max_variable_bound_violation": 0.0, - "num_nodes": 1999468624, - "num_simplex_iterations": 21951 - } - } - }, - "total_solve_time": 0.025009632110595703 - }, - "reqId": "eb753ac0-c6a2-4fda-9ad4-ee595cddf0ec" - } - - -An example with DataModel is available in the `Examples Notebooks Repository `_. - -The ``data`` argument to ``get_LP_solve`` may be a dictionary of the format shown in :doc:`MILP Open-API spec <../../open-api>`. More details on the response can be found under responses schema in :doc:`"/cuopt/request" and "/cuopt/solution" API spec <../../open-api>`. -They can be of different format as well, please check the documentation. - - -.. _aborting-thin-client: - -Aborting a Running Job in Thin Client -------------------------------------- - -:download:`abort_job_example.py ` - -.. literalinclude:: milp/examples/abort_job_example.py - :language: python - :linenos: - -======================================== -MILP CLI Examples -======================================== - -Generic MILP Example ---------------------- - -The only difference between this example and the prior LP example would be the variable types provided in data. - -.. code-block:: shell - - echo '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [3.0, 4.0, 2.7, 10.1] - }, - "constraint_bounds": { - "upper_bounds": [5.4, 4.9], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [0.2, 0.1], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "variable_names": ["x", "y"], - "variable_types": ["I", "I"], - "maximize": "False", - "solver_config": { - "time_limit": 30 - } - }' > data.json - -Invoke the CLI: - -.. code-block:: shell - - # Please update ip and port if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - cuopt_sh data.json -t LP -i $ip -p $port -sl -il - -In case the user needs to update solver settings through CLI, the option ``-ss`` can be used as follows: - -.. code-block:: shell - - # Please update ip and port if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - cuopt_sh data.json -t LP -i $ip -p $port -ss '{"time_limit": 5}' - -.. note:: - Batch mode is not supported for MILP. - -.. _aborting-cli: - -Aborting a Running Job In CLI ------------------------------ - -UUID that is returned by the solver while the solver is trying to find a solution so users can come back and check the status or query for results. - -This aborts a job with UUID if it's in running state. - -.. code-block:: bash - - # Please update ip and port if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - cuopt_sh -d -r -q -i $ip -p $port diff --git a/docs/cuopt/source/cuopt-server/examples/routing-examples.rst b/docs/cuopt/source/cuopt-server/examples/routing-examples.rst deleted file mode 100644 index b9eef8be19..0000000000 --- a/docs/cuopt/source/cuopt-server/examples/routing-examples.rst +++ /dev/null @@ -1,298 +0,0 @@ -======================================== -Routing Python Examples -======================================== - -The following example showcases how to use the ``CuOptServiceSelfHostClient`` to solve a simple routing problem. - -The OpenAPI specification for the server is available in :doc:`open-api spec <../../open-api>`. The example data is structured as per the OpenAPI specification for the server, please refer :doc:`OptimizeRoutingData under "POST /cuopt/request" <../../open-api>` under schema section. - -Generic Example ---------------- - -:download:`basic_routing_example.py ` - -.. literalinclude:: routing/examples/basic_routing_example.py - :language: python - :linenos: - -The response would be as follows: - - .. code-block:: json - :linenos: - - { - "response": { - "solver_response": { - "status": 0, - "num_vehicles": 1, - "solution_cost": 2.0, - "objective_values": { - "cost": 2.0 - }, - "vehicle_data": { - "0": { - "task_id": [ - "Depot", - "0", - "1", - "Depot" - ], - "arrival_stamp": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "type": [ - "Depot", - "Delivery", - "Delivery", - "Depot" - ], - "route": [ - 0, - 0, - 1, - 0 - ] - } - }, - "initial_solutions": [], - "dropped_tasks": { - "task_id": [], - "task_index": [] - } - }, - "total_solve_time": 0.1120915412902832 - }, - "reqId": "ebd378a3-c02a-47f3-b0a1-adec81be7cdd" - } - - -.. _initial-solution-in-python: - -Initial Solution ----------------- - -Previously run solutions or uploaded solutions can be used as the initial solution for new requests using previously run reqIds as follows: - -:download:`initial_solution_example.py ` - -.. literalinclude:: routing/examples/initial_solution_example.py - :language: python - :linenos: - - -The initial solution in the response is ``not accepted``, because the problem is too small, and the optimal solution is found even before cuOpt could use an initial solution. - -The response would be as follows: - - .. code-block:: json - :linenos: - - { - "response": { - "solver_response": { - "status": 0, - "num_vehicles": 1, - "solution_cost": 2.0, - "objective_values": { - "cost": 2.0 - }, - "vehicle_data": { - "0": { - "task_id": [ - "Depot", - "0", - "1", - "Depot" - ], - "arrival_stamp": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "type": [ - "Depot", - "Delivery", - "Delivery", - "Depot" - ], - "route": [ - 0, - 0, - 1, - 0 - ] - } - }, - "initial_solutions": [ - "not accepted", - ], - "dropped_tasks": { - "task_id": [], - "task_index": [] - } - }, - "total_solve_time": 0.06160402297973633 - }, - "reqId": "ebd378a3-c02a-47f3-b0a1-adec81be7cdd" - } - -The ``data`` argument to ``get_optimized_routes`` may be a dictionary of the format shown in :doc:`Get Routes Open-API spec <../../open-api>`. -It may also be the path of a file containing such a dictionary as JSON or written using the Python *msgpack* module. -A JSON file may optionally be compressed with zlib. More details on the responses can be found under the responses schema in :doc:`"get /cuopt/request" and "get /cuopt/solution" API spec <../../open-api>`. - - -To enable HTTPS: - -* In the case of the server using public certificates, simply enable https. - - .. code-block:: python - :linenos: - - from cuopt_sh_client import CuOptServiceSelfHostClient - - data = {"cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}}, - "task_data": {"task_locations": [0,1]}, - "fleet_data": {"vehicle_locations": [[0,0],[0,0]]}} - - # If cuOpt is not running on localhost:5000, edit ip and port parameters - cuopt_service_client = CuOptServiceSelfHostClient( - ip="localhost", - port=5000, - use_https=True - ) - -* In the case of a self-signed certificate, provide the complete path to the certificate. - - .. code-block:: python - :linenos: - - from cuopt_sh_client import CuOptServiceSelfHostClient - - data = {"cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}}, - "task_data": {"task_locations": [0,1]}, - "fleet_data": {"vehicle_locations": [[0,0],[0,0]]}} - - # If cuOpt is not running on localhost:5000, edit ip and port parameters - cuopt_service_client = CuOptServiceSelfHostClient( - ip="localhost", - port=5000, - use_https=True, - self_signed_cert=/complete/path/to/certificate - ) - - - You can generate a self-signed certificate easily as follows: - - .. code-block:: shell - - openssl genrsa -out ca.key 2048 - openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ca.crt - - openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=*.example.com" -out server.csr - openssl x509 -req -extfile <(printf "subjectAltName=DNS:example.com,DNS:www.example.com") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt - - - ``server.crt`` and ``server.key`` are meant for server, ``ca.crt`` is meant for client. - - -More examples are available in the `Examples Notebooks Repository `_. - -Aborting a Running Job in Thin Client -------------------------------------- - -Please refer to the :ref:`aborting-thin-client` for more details. - -======================================== -Routing CLI Examples -======================================== - -Create a ``data.json`` file containing this sample data: - -Routing Example ---------------- - -.. code-block:: shell - - echo '{"cost_matrix_data": {"data": {"0": [[0, 1], [1, 0]]}}, - "task_data": {"task_locations": [0, 1]}, - "fleet_data": {"vehicle_locations": [[0, 0], [0, 0]]}}' > data.json - -Invoke the CLI. - -.. code-block:: shell - - # client's default ip address for cuOpt is localhost:5000 if ip/port are not specified - export ip="localhost" - export port=5000 - cuopt_sh data.json -i $ip -p $port - -.. _initial-solution-in-cli: - -Initial Solution in CLI ------------------------ - -To use a previous solution as an initial solution for a new request ID, you are required to save the previous solution, which can be accomplished use option ``-k``. Use the previous reqId in the next request as follows: - -.. code-block:: shell - - # Please update ip and port if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - reqId=$(cuopt_sh data.json -i $ip -p $port -k | sed "s/'/\"/g" | jq -r '.reqId') - - cuopt_sh data.json -i $ip -p $port -id $reqId - - # delete previous saved solutions using follwing command - cuopt_sh -i $ip -p $port -d $reqId - - -Uploading a Solution --------------------- - -Users can also upload a solution which might have been saved for later runs. - -.. code-block:: shell - - # Please update ip and port if the server is running on a different IP address or port - export ip="localhost" - export port=5000 - - # Save solution to a file - cuopt_sh data.json -i $ip -p $port | sed "s/'/\"/g" > solution.json - - # Upload the solution and get request-id generated for that - reqId=$(cuopt_sh solution.json -us -i $ip -p $port | sed "s/'/\"/g" | jq -r '.reqId') - - # Use this request id for initial solution - cuopt_sh data.json -i $ip -p $port -id $reqId - - # delete previous saved solutions using follwing command - cuopt_sh -i $ip -p $port -ds $reqId - - -Aborting a Running Job In CLI ------------------------------ - -Please refer to the :ref:`aborting-cli` for more in MILP Example. - -.. note:: - Please use solver settings while using .mps files. - -To enable HTTPS ----------------- - -* In the case of the server using public certificates, simply enable https. - - .. code-block:: shell - - cuopt_sh data.json -s -i $ip -p $port - -* In the case of a self-signed certificate, provide the complete path to the certificate. - - .. code-block:: shell - - cuopt_sh data.json -s -c /complete/path/to/certificate -i $ip -p $port diff --git a/docs/cuopt/source/cuopt-server/images/aws1.png b/docs/cuopt/source/cuopt-server/images/aws1.png deleted file mode 100644 index c48a117a4f..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/aws1.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/aws2.png b/docs/cuopt/source/cuopt-server/images/aws2.png deleted file mode 100644 index 39fb380c66..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/aws2.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/aws3.png b/docs/cuopt/source/cuopt-server/images/aws3.png deleted file mode 100644 index c9ab08d9e4..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/aws3.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/aws4.png b/docs/cuopt/source/cuopt-server/images/aws4.png deleted file mode 100644 index 0028b90ff8..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/aws4.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/azure1.png b/docs/cuopt/source/cuopt-server/images/azure1.png deleted file mode 100644 index c859aaede8..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/azure1.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/azure2.png b/docs/cuopt/source/cuopt-server/images/azure2.png deleted file mode 100644 index 80f3fe55e0..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/azure2.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/azure3.png b/docs/cuopt/source/cuopt-server/images/azure3.png deleted file mode 100644 index 1242ac2d2c..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/azure3.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/azure4.png b/docs/cuopt/source/cuopt-server/images/azure4.png deleted file mode 100644 index 14e2029174..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/azure4.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/azure5.png b/docs/cuopt/source/cuopt-server/images/azure5.png deleted file mode 100644 index 00fbb23bba..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/azure5.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/azure6.png b/docs/cuopt/source/cuopt-server/images/azure6.png deleted file mode 100644 index 55ec667c82..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/azure6.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/images/cuOpt-self-hosted.png b/docs/cuopt/source/cuopt-server/images/cuOpt-self-hosted.png deleted file mode 100644 index 9915c69b78..0000000000 Binary files a/docs/cuopt/source/cuopt-server/images/cuOpt-self-hosted.png and /dev/null differ diff --git a/docs/cuopt/source/cuopt-server/index.rst b/docs/cuopt/source/cuopt-server/index.rst deleted file mode 100644 index 0d9c7a277f..0000000000 --- a/docs/cuopt/source/cuopt-server/index.rst +++ /dev/null @@ -1,54 +0,0 @@ -Server -====== - -The **NVIDIA cuOpt self-hosted server** is a **REST** (HTTP/JSON) service for integrations that speak HTTP. Use :doc:`quick-start` for deployment, :doc:`server-api/index` for the API, and :doc:`client-api/index` for clients (including cuopt-sh-client). - -For **gRPC remote execution** (Python, C API, ``cuopt_cli``, or custom clients to ``cuopt_grpc_server``), see :doc:`../cuopt-grpc/index` — it uses a different protocol and is not part of the HTTP REST surface. - -.. image:: images/cuOpt-self-hosted.png - :width: 500 - :align: center - -Please refer to the following sections for REST deployment, API reference, and examples. - -.. toctree:: - :caption: Quickstart - :name: Quickstart - :titlesonly: - - quick-start.rst - -.. toctree:: - :caption: Server API - :name: Server API - :titlesonly: - - Server-API - -.. toctree:: - :caption: Client API - :name: Client API - :titlesonly: - - Client-API - -.. toctree:: - :caption: Examples - :name: Examples - :titlesonly: - - Examples - -.. toctree:: - :caption: CSP Guides - :name: CSP Guides - :titlesonly: - - CSP-Guides - -.. toctree:: - :caption: NIM Operator - :name: NIM Operator - :titlesonly: - - NIM-Operator diff --git a/docs/cuopt/source/cuopt-server/nim-operator/configuration.rst b/docs/cuopt/source/cuopt-server/nim-operator/configuration.rst deleted file mode 100644 index 21ec2a5fad..0000000000 --- a/docs/cuopt/source/cuopt-server/nim-operator/configuration.rst +++ /dev/null @@ -1,289 +0,0 @@ -.. _cuopt-nim-configuration: - -Configuration Guide -=================== - -This guide covers configuration options for the CuOpt NIM Operator deployment. - -Image Configuration -------------------- - -CuOpt Image Versions -^^^^^^^^^^^^^^^^^^^^ - -Update the image tag in ``cuopt-nimservice.yaml``: - -.. list-table:: - :header-rows: 1 - :widths: 30 70 - - * - CUDA Version - - Image Tag - * - CUDA 12.x - - ``26.6.0-cu12`` - * - CUDA 13.x - - ``26.6.0-cu13`` - -.. code-block:: yaml - - spec: - image: - repository: nvcr.io/nvidia/cuopt/cuopt - tag: "26.6.0-cu12" - pullPolicy: IfNotPresent - -Resource Configuration ----------------------- - -GPU Resources -^^^^^^^^^^^^^ - -Configure GPU allocation: - -.. code-block:: yaml - - spec: - resources: - limits: - nvidia.com/gpu: 1 # Number of GPUs - -Memory Resources -^^^^^^^^^^^^^^^^ - -For workloads requiring specific memory allocation: - -.. code-block:: yaml - - spec: - resources: - limits: - nvidia.com/gpu: 1 - memory: "32Gi" - requests: - memory: "16Gi" - -Environment Variables ---------------------- - -CuOpt supports several environment variables for configuration: - -.. code-block:: yaml - - spec: - env: - - name: CUOPT_DATA_DIR - value: /model-store - - name: CUOPT_SERVER_LOG_LEVEL - value: info # Options: debug, info, warning, error - - name: CUOPT_SERVER_PORT - value: "8000" - -Storage Configuration ---------------------- - -The deployment optionally uses persistent storage so that datasets can be passed through the filesystem -rather than over http. If data is sent over http (the default), this storage is not needed. - -.. code-block:: yaml - - spec: - storage: - pvc: - create: true - size: 10Gi - storageClass: "" # Uses default storage class - volumeAccessMode: "ReadWriteOnce" - -For custom storage class: - -.. code-block:: yaml - - spec: - storage: - pvc: - create: true - size: 20Gi - storageClass: "fast-ssd" - volumeAccessMode: "ReadWriteOnce" - -Networking Configuration ------------------------- - -Service Configuration -^^^^^^^^^^^^^^^^^^^^^ - -Default ClusterIP service: - -.. code-block:: yaml - - spec: - expose: - service: - type: ClusterIP - port: 8000 - -For NodePort access: - -.. code-block:: yaml - - spec: - expose: - service: - type: NodePort - port: 8000 - nodePort: 30800 - -For LoadBalancer (cloud environments): -.. note:: Currently the cuopt service does not support scaling; there can only be 1 instance of the pod per service. Therefore a LoadBalancer service is unnecessary. - -.. code-block:: yaml - - spec: - expose: - service: - type: LoadBalancer - port: 8000 - -Ingress Configuration -^^^^^^^^^^^^^^^^^^^^^ - -To expose CuOpt externally via ingress: - -.. code-block:: yaml - - spec: - expose: - service: - type: ClusterIP - port: 8000 - ingress: - enabled: true - spec: - ingressClassName: nginx - rules: - - host: cuopt.example.com - http: - paths: - - backend: - service: - name: cuopt-service - port: - number: 8000 - path: / - pathType: Prefix - -With TLS: - -.. code-block:: yaml - - spec: - expose: - ingress: - enabled: true - spec: - ingressClassName: nginx - tls: - - hosts: - - cuopt.example.com - secretName: cuopt-tls-secret - rules: - - host: cuopt.example.com - http: - paths: - - backend: - service: - name: cuopt-service - port: - number: 8000 - path: / - pathType: Prefix - -Scaling Configuration ---------------------- - -Currently the cuOpt service does not support scaling. Only a single instance of the pod per service is supported. - -Health Probes -------------- - -Liveness Probe -^^^^^^^^^^^^^^ - -Determines if the container is running: - -.. code-block:: yaml - - spec: - livenessProbe: - enabled: true - probe: - failureThreshold: 3 - httpGet: - path: /v2/health/live - port: api - initialDelaySeconds: 15 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - -Readiness Probe -^^^^^^^^^^^^^^^ - -Determines if the container is ready to accept traffic: - -.. code-block:: yaml - - spec: - readinessProbe: - enabled: true - probe: - failureThreshold: 30 - httpGet: - path: /v2/health/ready - port: api - initialDelaySeconds: 30 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - -Startup Probe -^^^^^^^^^^^^^ - -For slower starting containers: - -.. code-block:: yaml - - spec: - startupProbe: - enabled: true - probe: - failureThreshold: 30 - httpGet: - path: /v2/health/ready - port: api - periodSeconds: 10 - -Monitoring Configuration ------------------------- - -Enable Prometheus metrics and ServiceMonitor: - -.. code-block:: yaml - - spec: - metrics: - enabled: true - serviceMonitor: - additionalLabels: - release: kube-prometheus-stack - -Full Configuration Example --------------------------- - -Here's a complete production-ready configuration: - -:download:`cuopt-nimservice-full.yaml ` - -.. literalinclude:: guide/cuopt-nimservice-full.yaml - :language: yaml - :linenos: diff --git a/docs/cuopt/source/cuopt-server/nim-operator/deployment.rst b/docs/cuopt/source/cuopt-server/nim-operator/deployment.rst deleted file mode 100644 index e51e09e88e..0000000000 --- a/docs/cuopt/source/cuopt-server/nim-operator/deployment.rst +++ /dev/null @@ -1,262 +0,0 @@ -.. _cuopt-nim-deployment: - -Deployment Guide -================ - -This guide covers deploying CuOpt using the NIM Operator. - -Automated Deployment --------------------- - -The easiest way to deploy CuOpt is using the provided deployment script. - -Using the Deploy Script -^^^^^^^^^^^^^^^^^^^^^^^ - -:download:`deploy.sh ` - -.. literalinclude:: guide/deploy.sh - :language: bash - :linenos: - :lines: 1-50 - :caption: deploy.sh (showing first 50 lines) - -1. Set your NGC API Key: - - .. code-block:: bash - - export NGC_API_KEY= - -2. Run the deployment script: - - .. code-block:: bash - - ./deploy.sh - -3. With custom options: - - .. code-block:: bash - - ./deploy.sh --namespace my-cuopt --wait 600 - -Script Options -^^^^^^^^^^^^^^ - -.. code-block:: text - - Usage: deploy.sh [OPTIONS] - - Options: - -n, --namespace NAME Kubernetes namespace (default: nim-service) - -t, --tag TAG CuOpt image tag - -u, --uninstall Uninstall CuOpt deployment - -s, --skip-prerequisites Skip prerequisite checks - -w, --wait SECONDS Timeout for waiting on resources (default: 300) - -h, --help Show help message - -Manual Deployment ------------------ - -If you prefer to deploy manually, follow these steps. - -Step 1: Create Namespace -^^^^^^^^^^^^^^^^^^^^^^^^ - -:download:`namespace.yaml ` - -.. literalinclude:: guide/namespace.yaml - :language: yaml - :linenos: - -Apply the namespace: - -.. code-block:: bash - - kubectl apply -f namespace.yaml - -Step 2: Create Secrets -^^^^^^^^^^^^^^^^^^^^^^ - -Create the image pull secret: - -.. code-block:: bash - - kubectl create secret -n nim-service docker-registry ngc-secret \ - --docker-server=nvcr.io \ - --docker-username='$oauthtoken' \ - --docker-password=${NGC_API_KEY} - -Create the NGC API key secret: - -.. code-block:: bash - - kubectl create secret -n nim-service generic ngc-api-secret \ - --from-literal=NGC_API_KEY=${NGC_API_KEY} - -Step 3: Deploy CuOpt NIMService -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:download:`cuopt-nimservice.yaml ` - -.. literalinclude:: guide/cuopt-nimservice.yaml - :language: yaml - :linenos: - -Apply the NIMService: - -.. code-block:: bash - - kubectl apply -f cuopt-nimservice.yaml - -Verifying the Deployment ------------------------- - -Check NIMService Status -^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl get nimservice -n nim-service - -Expected output: - -.. code-block:: text - - NAME STATE AGE - cuopt-service Ready 5m - -Check Pods -^^^^^^^^^^ - -.. code-block:: bash - - kubectl get pods -n nim-service - -Check Service -^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl get service -n nim-service - -Expected output: - -.. code-block:: text - - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - cuopt-service ClusterIP 10.101.219.185 8000/TCP 5m - -Check Logs -^^^^^^^^^^ - -.. code-block:: bash - - kubectl logs -f deployment/cuopt-service -n nim-service - -You should see output indicating the server is running: - -.. code-block:: text - - 2025-11-19 20:52:50.655 INFO cuopt server version 25.10.01 - 2025-11-19 20:52:50.767 INFO Application startup complete. - 2025-11-19 20:52:50.767 INFO Uvicorn running on http://0.0.0.0:5000 - -Testing the Deployment ----------------------- - -Port Forward for Local Testing -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl port-forward svc/cuopt-service -n nim-service 8000:8000 - -Then access the service at ``http://localhost:8000``. - -Test with cuopt_sh CLI -^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - # Get the service ClusterIP - CUOPT_IP=$(kubectl get svc cuopt-service -n nim-service -o jsonpath='{.spec.clusterIP}') - - # Run a test - cuopt_sh -i $CUOPT_IP -t LP /path/to/your/problem.mps - -Example output: - -.. code-block:: text - - 2025-11-19 21:29:22.391 cuopt_sh_client.cuopt_self_host_client INFO Optimal - {'response': {'solver_response': {'status': 'Optimal', ...}}} - -Health Endpoints -^^^^^^^^^^^^^^^^ - -* Liveness: ``GET /v2/health/live`` -* Readiness: ``GET /v2/health/ready`` - -.. code-block:: bash - - curl http://localhost:8000/v2/health/ready - -Cleanup -------- - -Using the Script -^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - ./deploy.sh --uninstall - -Manual Cleanup -^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl delete -f cuopt-nimservice.yaml - kubectl delete secret ngc-secret ngc-api-secret -n nim-service - kubectl delete namespace nim-service - -Troubleshooting ---------------- - -Pod Not Starting -^^^^^^^^^^^^^^^^ - -Check pod events: - -.. code-block:: bash - - kubectl describe pod -l app=cuopt-service -n nim-service - -Check GPU operator status: - -.. code-block:: bash - - kubectl get pods -n gpu-operator - -Image Pull Errors -^^^^^^^^^^^^^^^^^ - -Verify your NGC credentials: - -.. code-block:: bash - - kubectl get secret ngc-secret -n nim-service -o yaml - -Health Check Failures -^^^^^^^^^^^^^^^^^^^^^ - -Check the readiness and liveness probe logs: - -.. code-block:: bash - - kubectl logs -f deployment/cuopt-service -n nim-service | grep -i health - -Next Steps ----------- - -See :ref:`configuration ` for advanced configuration options. diff --git a/docs/cuopt/source/cuopt-server/nim-operator/index.rst b/docs/cuopt/source/cuopt-server/nim-operator/index.rst deleted file mode 100644 index a20f798c72..0000000000 --- a/docs/cuopt/source/cuopt-server/nim-operator/index.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. _cuopt-nim-operator: - -NIM Operator Deployment -======================= - -This guide walks you through deploying NVIDIA cuOpt as a Kubernetes service using the -`NIM Operator `_. - -Overview --------- - -The NIM Operator simplifies the deployment and management of NVIDIA NIM microservices -on Kubernetes. This deployment method provides: - -* **Automated lifecycle management** - The operator handles scaling, updates, and health checks -* **Native Kubernetes integration** - Uses standard K8s patterns (CRDs, services, ingress) -* **GPU resource management** - Leverages the GPU Operator for optimal GPU utilization -* **Built-in monitoring** - Prometheus metrics and service monitors - -Supported GPUs --------------- - -The following NVIDIA GPUs are supported: - -.. list-table:: - :header-rows: 1 - :widths: 20 20 60 - - * - GPU - - Architecture - - Notes - * - A100 - - Ampere - - Recommended for production - * - H100 - - Hopper - - High performance - * - H200 - - Hopper - - High performance with extended memory - * - B200 - - Blackwell - - Latest generation - * - RTX 6000 Pro - - Ada Lovelace - - Workstation GPU - -Quick Start ------------ - -1. Complete all :ref:`prerequisites ` - -2. Set your NGC API Key: - - .. code-block:: bash - - export NGC_API_KEY= - -3. Run the deployment script: - - .. code-block:: bash - - ./deploy.sh - -4. Verify the deployment: - - .. code-block:: bash - - kubectl get nimservice -n nim-service - kubectl get pods -n nim-service - -Documentation Contents ----------------------- - -.. toctree:: - :maxdepth: 2 - - prerequisites - deployment - configuration - -Additional Resources --------------------- - -* `NVIDIA cuOpt Documentation `_ -* `NIM Operator Documentation `_ -* `GPU Operator Documentation `_ diff --git a/docs/cuopt/source/cuopt-server/nim-operator/prerequisites.rst b/docs/cuopt/source/cuopt-server/nim-operator/prerequisites.rst deleted file mode 100644 index cca32a5fcb..0000000000 --- a/docs/cuopt/source/cuopt-server/nim-operator/prerequisites.rst +++ /dev/null @@ -1,190 +0,0 @@ -.. _cuopt-nim-prerequisites: - -Prerequisites -============= - -This document covers all prerequisites needed before deploying CuOpt with the NIM Operator. - -Kubernetes Cluster Setup ------------------------- - -You need a Kubernetes cluster with GPU-enabled nodes. Choose one of the following -installation methods: - -Option 1: kubeadm (Manual Setup) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Standard Kubernetes installation using kubeadm. Follow the -`official Kubernetes documentation `_. - -Option 2: Cloud Native Stack (Recommended) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Use NVIDIA Cloud Native Stack Ansible playbooks for automated setup including GPU Operator: - -.. code-block:: bash - - git clone https://github.com/NVIDIA/cloud-native-stack.git - cd cloud-native-stack/playbooks - # Follow the playbook instructions - -This method automatically deploys necessary operators including the GPU Operator. - -Option 3: Minikube (Development/Testing) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -For local development and testing: - -.. code-block:: bash - - minikube start --driver=docker --gpus all - -GPU Operator Installation -------------------------- - -If not using Cloud Native Stack, install the GPU Operator manually. - -Add NVIDIA Helm Repository -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - helm repo add nvidia https://helm.ngc.nvidia.com/nvidia - helm repo update - -Install GPU Operator -^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - helm install --wait --generate-name \ - -n gpu-operator --create-namespace \ - nvidia/gpu-operator - -This typically takes 3-5 minutes to install the driver and set up the cloud native -stack for GPU usage. - -Verify GPU Operator -^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl get pods -n gpu-operator - -All pods should be in ``Running`` state. - -Storage Provisioner -------------------- - -CuOpt requires persistent storage. Deploy a storage provisioner if your cluster -doesn't have one. - -Local Path Provisioner (Development/Single Node) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml - -Wait for the provisioner to be ready: - -.. code-block:: bash - - kubectl rollout status deployment/local-path-provisioner -n local-path-storage --timeout=120s - -Set Default Storage Class -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' - -Verify Storage Class -^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl get storageclass - -You should see ``local-path`` marked as ``(default)``. - -Production Storage Options -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -For production deployments, consider: - -* **Cloud providers**: Use native storage classes (AWS EBS, GCP PD, Azure Disk) -* **On-premises**: Longhorn, OpenEBS, Rook-Ceph - -NIM Operator Installation -------------------------- - -Create Namespace -^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl create namespace nim-operator - -Install NIM Operator -^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - helm upgrade --install nim-operator nvidia/k8s-nim-operator \ - -n nim-operator \ - --version=3.0.2 - -Verify Installation -^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - kubectl get pods -n nim-operator - kubectl get crd | grep nvidia - -You should see the ``nimservices.apps.nvidia.com`` CRD registered. - -NGC API Key ------------ - -You need an NGC API key to pull NVIDIA container images. - -Obtain NGC API Key -^^^^^^^^^^^^^^^^^^ - -1. Go to `NGC `_ -2. Sign in or create an account -3. Navigate to **Setup** → **API Key** -4. Generate a new API key - -Set Environment Variable -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - export NGC_API_KEY= - -For persistent configuration, add to your shell profile: - -.. code-block:: bash - - echo 'export NGC_API_KEY=' >> ~/.bashrc - source ~/.bashrc - -Verification Checklist ----------------------- - -Before proceeding with CuOpt deployment, verify: - -* Kubernetes cluster is running (``kubectl cluster-info``) -* GPU nodes are available (``kubectl get nodes -l nvidia.com/gpu.present=true``) -* GPU Operator pods are running (``kubectl get pods -n gpu-operator``) -* Storage class is configured (``kubectl get storageclass``) -* NIM Operator is installed (``kubectl get pods -n nim-operator``) -* NGC API key is set (``echo $NGC_API_KEY``) - -Next Steps ----------- - -Once all prerequisites are met, proceed to :ref:`deployment `. diff --git a/docs/cuopt/source/cuopt-server/quick-start.rst b/docs/cuopt/source/cuopt-server/quick-start.rst deleted file mode 100644 index ee27428df9..0000000000 --- a/docs/cuopt/source/cuopt-server/quick-start.rst +++ /dev/null @@ -1,196 +0,0 @@ -================= -Quickstart Guide -================= - -Installation -============ - -Choose your install method below; the selector is pre-set for the Server. Copy the command and run it in your environment. See :doc:`../install` for all interfaces and options. - -.. install-selector:: - :default-iface: server - -.. _container-from-nvidia-ngc: - -Container from NVIDIA NGC -------------------------- - -Step 1: Get a subscription for `NVIDIA AI Enterprise (NVAIE) `_ to get the cuOpt container to host in your cloud. - -Step 2: Once given access, users can find `cuOpt container `_ in the NGC catalog. - -Step 3: Access NGC registry: - -* Log into NGC using the invite and choose the appropriate NGC org. -* Generate an NGC API key from settings. If you have not generated an API Key, you can generate it by going to the Setup option in your profile and choose Get API Key. Store this or generate a new one next time. More information can be found `here `_. - -Step 4: Pull the container: - -* Go to the container section for cuOpt and copy the pull tag for the latest image. -* Log into the nvcr.io container registry in your cluster setup, using the NGC API key as shown below. - - .. code-block:: bash - - docker login nvcr.io - Username: $oauthtoken - Password: - -* Pull the container - - .. code-block:: bash - - docker pull CONTAINER_IMAGE_PATH - - -The container includes both the Python API and self-hosted server components. To run the container: - -.. code-block:: bash - - docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 - -NVIDIA Launchable -------------------- - -NVIDIA cuOpt can be tested with `NVIDIA Launchable `_ with `example notebooks `_. For more details, please refer to the `NVIDIA Launchable documentation `_. - -Smoke Test ----------- - -After installation, you can verify that cuOpt Server is working correctly by running a simple test. - -.. note:: - - The following example is for running the server locally. If you are using the container approach, you should comment out the server start and kill commands in the script below since the server is already running in the container. - -The following example is testing with a simple routing problem constuctured as Json request and sent over HTTP to the server using ``curl``.This example is running server with few configuration options such as ``--ip`` and ``--port``. -Additional configuration options for server can be found in :doc:`Server CLI `. - - -Install jq and curl for basic HTTP requests and parsing JSON responses - -.. code-block:: bash - - sudo apt install jq curl - -Run the server and test - -.. code-block:: bash - - # Set the server IP and port to be used - SERVER_IP=0.0.0.0 - SERVER_PORT=8000 - - # Start server and store PID - python3 -m cuopt_server.cuopt_service --ip $SERVER_IP --port $SERVER_PORT > cuopt_server.log 2>&1 & - SERVER_PID=$! - - # Check if cuOpt server is ready - for i in {1..5}; do - if [ "$(curl -s -o /dev/null -w "%{http_code}" http://${SERVER_IP}:${SERVER_PORT}/cuopt/health)" = "200" ]; then - echo "cuOpt server is ready" - break - fi - if [ $i -eq 5 ]; then - echo "Error: cuOpt server failed to start" - exit 1 - fi - sleep 1 - done - - # Test the server with sample routing problem - # Use /cuopt/request to submit a request to the server - REQID=$(curl --location "http://${SERVER_IP}:${SERVER_PORT}/cuopt/request" \ - --header 'Content-Type: application/json' \ - --header "CLIENT-VERSION: custom" \ - -d '{ - "cost_matrix_data": {"data": {"0": [[0, 1], [1, 0]]}}, - "task_data": {"task_locations": [1], "demand": [[1]], "task_time_windows": [[0, 10]], "service_times": [1]}, - "fleet_data": {"vehicle_locations":[[0, 0]], "capacities": [[2]], "vehicle_time_windows":[[0, 20]] }, - "solver_config": {"time_limit": 2} - }' | jq -r '.reqId') - - # Verify we got a 200 response and reqId - if [ -z "$REQID" ]; then - echo "Error: Failed to get reqId from server" - exit 1 - else - echo "Successfully submitted request with ID: $REQID" - fi - - # Poll for results - # Use /cuopt/solution/${REQID} to poll for results - for i in {1..5}; do - RESPONSE=$(curl --location "http://${SERVER_IP}:${SERVER_PORT}/cuopt/solution/${REQID}" \ - --header 'Content-Type: application/json' \ - --header "CLIENT-VERSION: custom") - - if echo "$RESPONSE" | jq -e 'has("response")' > /dev/null 2>&1; then - echo "Got solution response:" - echo "$RESPONSE" | jq '.' 2>/dev/null || echo "$RESPONSE" - break - else - echo "Response status:" - echo "$RESPONSE" | jq '.' 2>/dev/null || echo "$RESPONSE" - fi - - if [ $i -eq 5 ]; then - echo "Error: Timed out waiting for solution" - exit 1 - fi - - echo "Waiting for solution..." - sleep 1 - done - - # Shutdown the server - kill $SERVER_PID - -The Open API specification for the server is available in :doc:`open-api spec <../open-api>`. - -Example Response: - -.. code-block:: json - - { - "response": { - "solver_response": { - "status": 0, - "num_vehicles": 1, - "solution_cost": 2, - "objective_values": { - "cost": 2 - }, - "vehicle_data": { - "0": { - "task_id": [ - "Depot", - "0", - "Depot" - ], - "arrival_stamp": [ - 0, - 1, - 3 - ], - "type": [ - "Depot", - "Delivery", - "Depot" - ], - "route": [ - 0, - 1, - 0 - ] - } - }, - "initial_solutions": [], - "dropped_tasks": { - "task_id": [], - "task_index": [] - } - }, - "total_solve_time": 0.10999655723571777 - }, - "reqId": "afea72c2-6c76-45ce-bcf7-0d55049f32e4" - } diff --git a/docs/cuopt/source/cuopt-server/server-api/index.rst b/docs/cuopt/source/cuopt-server/server-api/index.rst deleted file mode 100644 index bdb5a95bad..0000000000 --- a/docs/cuopt/source/cuopt-server/server-api/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -======================================== -Server API -======================================== - -This section contains details on Server options supported and open-api specification for the server. - -.. toctree:: - :maxdepth: 3 - :caption: Server API Overview - :name: Server API Overview - :titlesonly: - - server-cli.rst - ../../open-api.rst - ../../convex-settings.rst - ../../mip-settings.rst diff --git a/docs/cuopt/source/faq.rst b/docs/cuopt/source/faq.rst deleted file mode 100644 index 77d9785717..0000000000 --- a/docs/cuopt/source/faq.rst +++ /dev/null @@ -1,470 +0,0 @@ -============================== -FAQ -============================== - -General FAQ ------------------------------- - -.. dropdown:: Where can I find cuOpt container images? - - There are two options: - - NVIDIA Docker Hub (https://hub.docker.com/r/nvidia/cuopt) - - NVIDIA NGC registry (https://catalog.ngc.nvidia.com/orgs/nvidia/cuopt/containers/cuopt/tags) with an NVAIE license or NVIDIA Developer Program membership. - -.. dropdown:: How to get a NVAIE license? - - Please refer to `NVIDIA NVAIE `_ for more information. - -.. dropdown:: How to access NGC registry? - - With an NVAIE license or NVIDIA Developer Program membership, you can access the `NGC registry `_ for cuOpt container images. - -.. dropdown:: How to pull cuOpt container images from NGC registry? - - 1. Log into NGC using the invite and choose the appropriate NGC org. - - 2. Generate an NGC API key from settings. If you have not generated an API Key, you can generate it by going to the Setup option in your profile and choose `Get API Key `_. Store this or generate a new one next time. - - 3. Go to the container section for cuOpt and copy the pull tag for the latest image. - - Within the Select a tag dropdown, locate the container image release that you want to run. - - Click the Copy Image Path button to copy the container image path. - - 4. Log into the nvcr.io container registry in your cluster setup, using the NGC API key as shown below. - - .. code-block:: bash - - docker login nvcr.io - Username: $oauthtoken - Password: - - 5. Pull the cuOpt container image. - - .. code-block:: bash - - docker pull - -.. dropdown:: Do I need a GPU to use cuOpt? - - Yes, please refer to :doc:`system requirements ` for GPU specifications. You can acquire a cloud instance with a supported GPU and launch cuOpt; alternatively, you can launch it in your local machine if it meets the requirements. - -.. dropdown:: Does cuOpt use multiple GPUs/multi-GPUs/multi GPUs? - - #. Yes, in cuOpt self-hosted server, a solver process per GPU can be configured to run multiple solvers. Requests are accepted in a round-robin queue. More details are available in :doc:`server api `. - #. There is no support for leveraging multiple GPUs to solve a single problem or oversubscribing a single GPU for multiple solvers. - -.. dropdown:: The cuOpt Service is not starting: Issue with port? - - 1. Check the logs for the container (see cuOpt service monitoring below). - - Is port 5000 already in use? - - If port 5000 is unavailable, the logs will contain an error like this - - .. code-block:: bash - - ERROR: [Errno 98] error while attempting to bind on address ('0.0.0.0', 5000): address already in use” - - - 2. Try to locate the process that is using port 5000 and stop it if possible. A tool like ``netstat`` run as the root user can help identify ports mapped to processes, and ``docker ps -a`` will show running containers. - - 3. Alternatively, use port mapping to launch cuOpt on a different port such as 5001 (note the omission of ``--network=host`` flag): - - 4. If running locally, you can also use ``ps -aux | grep cuopt_server`` to find the process and kill it. - - .. code-block:: bash - - docker run -d --rm --gpus all -p 5001:5000 - -.. dropdown:: Why is NVIDIA cuOpt running longer than the supplied time limit? - - #. The time limit supplied governs the run time of the solver only, but there are other overheads such as network delay, ETL, validation or the solver being busy with other requests. - - #. The complete round-trip solve time might be more than what was set. - -.. dropdown:: Why am I getting "libssl.so.3: cannot open shared object file" or "libcrypto.so.3: cannot open shared object file" when importing cuOpt? - - The cuOpt wheel links against OpenSSL 3 and intentionally does not bundle ``libssl.so.3`` / ``libcrypto.so.3``; the host must provide them. This keeps libcrypto and any FIPS provider (system or mounted) byte-version-matched at runtime. - - Most modern Linux distributions provide OpenSSL 3 out of the box: Ubuntu 22.04+, Debian 12+, RHEL/Rocky/Alma 9+, and Fedora 36+. For older distributions: - - - **RHEL / Rocky / Alma / CentOS 8**: install the ``openssl3`` runtime package from EPEL: - - .. code-block:: bash - - dnf install -y epel-release - dnf install -y openssl3 - # Verify - ldconfig -p | grep -E 'libssl\.so\.3|libcrypto\.so\.3' - - - **Ubuntu 20.04**: install OpenSSL 3 from a PPA or backport, or use the cuOpt container image (Ubuntu 22.04 base) instead. - - - **Other distributions**: install your distribution's OpenSSL 3 development/runtime package, or use the cuOpt container. - - After installing, re-run ``pip install`` (or restart the Python process) and the import should succeed. - -.. dropdown:: Why am I getting "libcuopt.so: cannot open shared object file: No such file or directory" error? - - This error indicates that the cuOpt shared library is not found. Please check the following: - - - The cuOpt is installed - - Use ``find / -name libcuopt.so`` to search for the library path from root directory. You might need to run this command as root user. - - If the library is found, please add it to the ``LD_LIBRARY_PATH`` environment variable as shown below: - - .. code-block:: bash - - export LD_LIBRARY_PATH=/path/to/cuopt/lib:$LD_LIBRARY_PATH - - - If the library is not found, it means it is not yet installed. Please check the cuOpt installation guide for more details. - -.. dropdown:: Is there a way to make cuOpt also account for other overheads in the same time limit provided? - - - We currently don't account for it, since many such overheads are relative and cannot be tracked properly. - -.. dropdown:: cuOpt is not running: Issue with GPU memory availability? - - #. If there are errors pertaining to ``rmm`` or errors that the service couldn't acquire GPU memory, there is a possibility that GPU memory is being consumed by another process. - - #. This can be observed using the command ``nvidia-smi``. - -.. dropdown:: The cuOpt service is not responding: What to check? - - 1. cuOpt microservice health check on the cuOpt host. - - Perform a health-check locally on the host running cuOpt: - - .. code-block:: bash - - curl -s -o /dev/null -w '%{http_code}\\n' localhost:5000/cuopt/health - - - If this command returns 200, cuOpt is running and listening on the specified port. - - - If this command returns something other than 200, check the following: - - - Check that a cuOpt container is running with ``docker ps``. - - Examine the cuOpt container log for errors. - - Did you include the ``--network=host`` or a ``-p`` port-mapping flag to docker when you launched cuOpt? If you used port mapping, did you perform the health check using the correct port? - - Restart cuOpt and see if that corrects the problem. - - 2. cuOpt microservice health-check from a remote host. - - If you are trying to reach cuOpt from a remote host, run the health check from the remote host and specify the IP address of the cuOpt host, for example: - - .. code-block:: bash - :linenos: - - curl -s -o /dev/null -w '%{http_code}\\n' :5000/cuopt/health - 200 - - If this command does not return 200, but a health check locally on the cuOpt host does return 200, the problem is a network configuration or firewall issue. The host is not reachable, or the cuOpt port is not open to incoming traffic. - -.. dropdown:: Certificate Validation Errors from Python client? - - #. This might happen mostly with cuOpt running in a cloud instance. - - #. It could be that you are behind a proxy that is generating a certificate chain and you need additional certificate authorities installed on your machine. - - You can examine the certificate chain returned on a connection with the following commands or something similar. If it looks like there are certificates in the chain that are issued by your own organization, contact your local IT admin, and ask them for the proper certificates to install on your machine. - - In this example, we will check the certificate chain being returned from a connection to NVCF at NVIDIA, but you can substitute a different address if you are trying to connect to an instance of cuOpt deployed in the cloud: - - .. code-block:: bash - :linenos: - - export MY_SERVER_ADDRESS=”api.nvcf.nvidia.com:443” - openssl s_client -showcerts -connect $MY_SERVER_ADDRESS /dev/null | sed -n -e '/BEGIN\ CERTIFICATE/,/END CERTIFICATE/ p' > test.pem - - while openssl x509 -noout -text; do :; done < test.pem.txt - -gRPC remote execution (``cuopt_grpc_server``) ------------------------------------------------ - -.. dropdown:: Where are log files for the gRPC server / StreamLogs? - - Workers write per-job solver logs under ``/tmp/cuopt_logs/job_.log``. The ``StreamLogs`` RPC tails that file. Operational limits and behavior are summarized in :doc:`gRPC server behavior `. - -.. dropdown:: What happens if a ``cuopt_grpc_server`` worker crashes? - - Jobs that worker was running are marked **FAILED**. The server monitor can detect the crash and spawn a replacement worker; other workers keep running. For more detail, see :doc:`gRPC server behavior ` and the contributor reference ``cpp/docs/grpc-server-architecture.md`` in the repository. - -.. dropdown:: Does ``CancelJob`` stop a solve immediately? - - Cancellation is honored **before** the solver starts. If the solve has already begun, it **runs to completion**; there is no mid-solve cancellation path. See :doc:`gRPC server behavior `. - -Routing FAQ ------------------------------- - -.. dropdown:: What is a Waypoint Graph? - - A waypoint graph is a weighted, directed graph where the weights symbolize cost. Unlike the cost matrix, this graph often represents more than just target locations, including intermediate decision points along a route (locations merely passed through). This method is commonly used for custom environments and indoor spaces, such as warehouses and factories, where the cost between target locations is dynamic or not easily quantifiable. A basic waypoint graph with four nodes is illustrated below: - - .. image:: images/faq-01.png - - .. code-block:: json - :linenos: - - { - "cost_waypoint_graph_data":{ - "waypoint_graph": { - "0": { - "offsets": [0, 1, 2, 5, 6], - "edges": [2, 2, 0, 1, 3, 2], - "weights": [2, 2, 2, 2, 2, 2] - } - } - } - - Graphs intended for input into cuOpt are shown in **Compressed Sparse Row (CSR)** format for efficiency. The translation from a more conventional (and human-readable) graph format, such as a weighted edge list, to CSR can be accomplished quickly, as depicted below: - - .. code-block:: python - :linenos: - - graph = { - 0:{ - "edges":[2], - "weights":[2]}, - 1:{ - "edges":[2], - "weights":[2]}, - 2:{ - "edges":[0, 1, 3], - "weights":[2, 2, 2]}, - 3:{ - "edges":[2], - "weights":[2]} - } - - def convert_to_csr(graph): - num_nodes = len(graph) - - offsets = [] - edges = [] - weights = [] - - cur_offset = 0 - for node in range(num_nodes): - offsets.append(cur_offset) - cur_offset += len(graph[node]["edges"]) - - edges = edges + graph[node]["edges"] - weights = weights + graph[node]["weights"] - - offsets.append(cur_offset) - - return offsets, edges, weights - - offsets, edges, weights = convert_to_csr(graph) - print(f"offsets = {offsets}") - print(f"edges = {edges}") - print(f"weights = {weights}") - -.. dropdown:: What is a mixed fleet? - - #. In some cases, not all vehicles within a fleet are identical. Some might travel faster, while others might incur unaffordable costs when traveling through certain areas. For example, we could have a fleet consisting of planes and trucks. - #. ``vehicle_types`` can be used along with data such as cost/time matrix for each of the vehicles. Given the example above, planes would have one cost/time matrix, while trucks would have a different cost/time matrix. - -.. dropdown:: How to get partially feasible solutions to infeasible problems? - - Use Prize collection, which associates each task with a prize and the solver will maximize the prize collected. This allows cuOpt to prioritize some tasks over others. - -.. dropdown:: What is a dimension mismatch error? - - Some of the metrics need to be equal in size; for example, the number of tasks and their demand. If they don't match, it means the problem is partially defined or there is an issue with the data. - -.. dropdown:: cuOpt resource estimates; how large a problem can I run with a given set of constraints? - - For the standard CVRPTW (Capacitated Vehicle Routing Problem with Time Windows) problem with real-world constraints, cuOpt can easily solve for 15K locations with the NVIDIA GPU A100/H100. - -.. dropdown:: Not getting the same solution in every run: Determinism? - - #. cuOpt routing solver is not deterministic, so the results might vary across multiple runs. Increasing the time limit set for the solver will increase the likelihood of getting identical results across multiple runs. - #. Also, there might be several different solutions with the same cost. - -.. dropdown:: How do we account for dynamic changing constraints? - - #. cuOpt is stateless and cannot handle dynamic constraints directly, but this can be resolved with modeling. - #. Dynamic reoptimization is used when there is a change in the conditions of the operation such as a vehicle getting broken, a driver calling in sick, a road block, traffic, or a high-priority order coming in. - #. The problem is prepped in such a way that the packages that are already en route are assigned to only those vehicles, and new and old deliveries will be added to this problem. Please refer to example notebooks in :doc:`cuOpt Resources ` to understand more about how to tackle this problem. - -.. dropdown:: Does cuOpt take an initial solution? - - Currently, cuOpt doesn't accept the initial solution. - -.. dropdown:: Do we need to normalize the data when creating a time window matrix? - - The units can be whatever the customer wants them to be: minutes, seconds, milliseconds, hours, and so on. It is the user's responsibility to normalize the data across the complete problem, so all time-related constraints use the same unit. For example, if the travel time matrix is given in minutes, we want to make sure time windows and service times are also given in minutes. - -.. dropdown:: Is there a way to prevent vehicles from traveling along the same path in a waypoint graph, or is there a way to prevent more than one vehicle from visiting a location, or even that a location is only visited one time by a single vehicle? - - Currently, we do not have such restrictions, and cuOpt tries to optimize for the fewest number of vehicles as the primary default objective. - -.. dropdown:: Travel time deviation: When using the same dataset, the travel time varies by a couple of seconds in different runs, but the distance remains the same. How can travel time deviate in multiple runs on the same data and distance remains constant? - - This is because travel time is not part of the objective, so we could have two solutions that are equivalent when picking the best solution. You can include total travel time (includes wait time) as part of the objective. - -.. dropdown:: There is no path between two locations, how do I input this information to the solver? - - #. Set high values compared to other actual values, not max of float type. - #. This will ensure this path would not be traversed since it will incur a huge cost. - -.. dropdown:: Floating point vs. integers for specifying task locations? - - The documentation says ``task_locations`` should be integers. But in the real world, latitude and longitude coordinates are floating point values. To explain this, read the following section. - - cuOpt expects that a user provides either: - - - A cost matrix and corresponding location indices. - - A waypoint graph and locations corresponding to waypoints as integers. - - So in either case, task locations are actually integer indices into another structure. - - If you have (lat, long) values, then you can generate a cost matrix using a map API. cuOpt does not directly connect to a third-party map engine, but that can be done outside of cuOpt and the resulting cost matrix passed in. - -.. dropdown:: Is it possible to define constraints such as refrigerated vehicles required for certain orders? - - Yes, you can define constraints to match vehicles to order type using ``vehicle_order_match``. Frozen goods are a great example. - -.. dropdown:: How do we model the following scenario: Pick up from multiple different locations and deliver to a single customer? - - This can be observed as a pickup and delivery problem. - -.. dropdown:: I know that the problem has a feasible solution, but cuOpt returns an infeasible solution. How do I avoid this? - - #. The time limit could be too short. - #. An infeasible solution always provides information about what constraints caused it and which constraint can be relaxed, which might give more hints. - -.. dropdown:: How to set prize collection to deliver as many orders as possible ? - - Set all prize values = 1 with a very high prize objective (like 10^6), and then set the other objective values for ``cost``, ``travel_time``, and ``route_variance`` proportional to each other for cuOpt to always return the best possible solution. - -.. dropdown:: What are the limitations of the routing solver? - - #. The routing solver capabilities are based on few factors: - - - The available GPU memory - - The size of the problem - - Number of locations - - Number of vehicles - - Number of tasks - - The complexity of the problem - - Number of demand and capacity constraints - - Number of time windows - - Number of vehicle types - - Number of breaks - - The time limit - - Depending on these factors, the problems that can be solved can vary, for example: - - - On a H100 SXM with 80GB memory, the maximum number of locations that routing solver can handle is 10,000. - - At the same time, depending on complexity, the solver might be able to handle more or less than 10,000 locations. - -Linear Programming FAQs ------------------------------- - -.. dropdown:: How small and how many problems can I give when using the batch mode? - - LP batch mode is deprecated; see :ref:`Batch Mode ` in - :doc:`convex-features`. - - The batch mode allows solving many LPs in parallel to try to fully utilize the GPU when LP problems are too small. Using H100 SXM, the problem should be of at least 1K elements, and giving more than 100 LPs will usually not increase performance. - -.. dropdown:: Can the solver run on dense problems? - - Yes, but we usually see great results on very large and sparse problems. - -.. dropdown:: How large can the problem be? - - If run on a H100 SXM 80GB (hardware used when using NVIDIA Cloud Functions), you can run the following sizes: - - - 4.5M rows/constraints; 4.5M columns/variables; and 900M non-zeros in the constraint matrix - - 36M rows/constraints; 36M columns/variables; and 720M non-zeros in the constraint matrix - -.. dropdown:: How can I get the best performance? - - There are several ways to tune the solver to get the best possible performance: - - - Hardware: If using self-hosted, you should use a recent server-grade GPU. We recommend H100 SXM (not the PCIE version). - - Tolerance: The set tolerance usually has a massive impact on performance. Try the lowest possible value using ``set_optimality_tolerance`` until you have reached your lowest possible acceptable accuracy. - - PDLP Solver mode: PDLP solver mode will change the way PDLP internally optimizes the problem. The mode choice can drastically impact how fast a specific problem will be solved. You should test the different modes to see which one fits your problem best. - - Batch mode: Deprecated for LP; prefer sequential ``cuopt.linear_programming.Solve`` calls (see :ref:`Batch Mode `). While available, batch mode can solve multiple LPs in parallel. - - Presolve: Presolve can reduce problem size and improve solve time. - -.. dropdown:: What solver mode should I choose? - - We cannot predict up-front which solver mode will work best for a particular problem. The only way to know is to test. Once you know a solver mode is good on a class of problems, it should also be good on other similar problems. - -.. dropdown:: What tolerance should I use? - - The choice entirely depends on the level of accuracy you need for your problem. A looser tolerance will always result in a faster result. For PDLP, 1e-2 relative tolerance is low accuracy, 1e-4 is regular, 1e-6 is high, and 1e-8 is very high. - -.. dropdown:: What are the limitations of the LP solver? - - #. There is no inherit limit imposed on the number of variables, number of constraints, or number of non-zeros you can have in a MILP or LP, except the restrictions due to the number of bits in an integer and the amount of memory in the CPU and GPU. - - Depending on these factors, the problems that can be solved can vary, for example: - - - On a H100 SXM with 80GB memory, here are few examples of the problems that can be solved: - - 10M rows/constraints, 10M columns/variables, and 2B non-zeros in the constraint matrix. - - 74.5M rows/constraints, 74.5M columns/variables, and 1.49B non-zeros in the constraint matrix. - -.. dropdown:: Does cuOpt implement presolve reductions? - - cuOpt supports presolve reductions using PSLP or Papilo for linear programming (LP) problems, and Papilo for mixed-integer programming (MIP) problems. For MIP problems, Papilo presolve is always enabled by default. For LP problems, PSLP presolve is always enabled by default. Presolve is controlled by the ``CUOPT_PRESOLVE`` setting. - -.. dropdown:: How do I use warm start with PDLP? - - To use warm start functionality with PDLP, you must explicitly disable presolve by setting ``CUOPT_PRESOLVE=0`` in solver_config. - This is required because presolve transforms the problem, and the warm start solution from the original problem - cannot be applied to the presolved problem. - -Mixed Integer Linear Programming FAQs --------------------------------------- - -.. dropdown:: How do I run the MIP solver with cuOpt? - - You can run the cuOpt MIP solver through the Python, C, CLI, or server APIs - by providing a model with integer or binary variables. The cuOpt MIP solver - is in **beta** and under active development. The solver currently excels at - finding high-quality feasible solutions quickly with GPU-accelerated primal - heuristics. Proving feasible solutions optimal remains under active - development. - -.. dropdown:: What are the limitations of the MILP solver? - - #. There is no inherit limit imposed on the number of variables, number of constraints, or number of non-zeros you can have in a MILP or LP, except the restrictions due to the number of bits in integer and the amount of memory in the CPU and GPU. - - Depending on these factors, the problems that can be solved can vary, for example: - - - On a H100 SXM with 80GB memory, this is the biggest dataset that was tested: - - 27 million non-zeros coefficients on a problem from MIPLIB2017. - -Container FAQs ------------------------------- - -.. dropdown:: How do I share only selected GPUs in a container? - - You can share only selected GPUs in a container by using the ``--gpus`` flag. For example, to share only the first GPU, you can use the following command: - - .. code-block:: bash - - docker run --gpus '"device=0,1"' - -.. dropdown:: How do I run cuOpt container with options set as environment variables? - - You can run cuOpt container with options set as environment variables by using the ``--env`` flag. For example, to set the time limit to 1000 seconds, you can use the following command: - - .. code-block:: bash - - mkdir data - mkdir result - docker run -v `pwd`/data:/cuopt_data \ - -v `pwd`/results:/cuopt_results \ - -e "CUOPT_DATA_DIR=/cuopt_data" \ - -e "CUOPT_RESULT_DIR=/cuopt_result" \ - -e CUOPT_MAX_RESULT=0 \ - -e CUOPT_SERVER_PORT=8081 \ - -p 8081:8081 \ - \ - /bin/bash -c "python -m cuopt_server.cuopt_server" diff --git a/docs/cuopt/source/hidden/limitations.rst b/docs/cuopt/source/hidden/limitations.rst deleted file mode 100644 index f09024745a..0000000000 --- a/docs/cuopt/source/hidden/limitations.rst +++ /dev/null @@ -1,39 +0,0 @@ -====================================== -NVIDIA cuOpt Limitations -====================================== - -Routing -======= - -#. Number of tasks: - - For ``H100`` : ``Tasks <= 15000`` per solve request. - - For ``A100`` : ``Tasks <= 10000`` per solve request. - -.. note:: - There may be cases where cuOpt might fail due to out of memory error in case the input problem has numerous constraints. - -#. Number of vehicles: - - ``Vehicles <= 3000`` per solve request is suggested. - -#. Capacity and demand constraints: - - Only ``3`` dimensions are supported. - -#. Time windows: - - All time windows are expected to be => 0, and all time windows are int32 types. So the time window range supported would be ``[0, 2^31-1]``. - - This translates to ``[January 1, 1970, 00:00:00, January 19, 2038, 03:14:07]`` in UTC. - -#. Type of vehicles, which also dictates number of cost and time matrix for each vehicle: - - ``Types <= 10`` is suggested. - -Linear Programming -================== - -- For ``H100``, - - 10M rows/constraints 10M columns/variables and 2B non-zeros in the constraint matrix - - 74.5M rows/constraints 74.5M columns/variables and 1.49B non-zeros in the constraint matrix - -Mixed Integer Linear Programming -================================ - -- Number of non-zeros/coefficient matrix size supported - - For ``H100`` - 27 million diff --git a/docs/cuopt/source/hidden/mps-api.rst b/docs/cuopt/source/hidden/mps-api.rst deleted file mode 100644 index ec2be8df66..0000000000 --- a/docs/cuopt/source/hidden/mps-api.rst +++ /dev/null @@ -1,8 +0,0 @@ -=============================== -cuOpt MPS/LP Parser API Reference -=============================== - -MPS/QPS/LP parser -------------------- - -.. autofunction:: cuopt.linear_programming.io.Read diff --git a/docs/cuopt/source/hidden/mps-overview.rst b/docs/cuopt/source/hidden/mps-overview.rst deleted file mode 100644 index fcdbb3dc24..0000000000 --- a/docs/cuopt/source/hidden/mps-overview.rst +++ /dev/null @@ -1,5 +0,0 @@ -========================= -NVIDIA cuOpt MPS Parser -========================= - -The cuOpt MPS parser is a CPU-based parser utility that helps you access data in much more structured way. diff --git a/docs/cuopt/source/hidden/parser_example.rst b/docs/cuopt/source/hidden/parser_example.rst deleted file mode 100644 index 9f79545e80..0000000000 --- a/docs/cuopt/source/hidden/parser_example.rst +++ /dev/null @@ -1,26 +0,0 @@ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -cuOpt problem file parser example -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -Example -------- - -Read MPS, QPS, or LP files (including ``.gz`` / ``.bz2`` compressed variants) -with :func:`~cuopt.linear_programming.Read`: - -.. code-block:: python - :linenos: - - from cuopt.linear_programming import Read - from cuopt.linear_programming.problem import Problem - - # MPS / QPS - mps_model = Read("good-mps-1.mps") - - # LP (plain or compressed) - lp_model = Read("good-mps-1.lp") - lp_gz = Read("good-mps-1.lp.gz") - - # High-level API - problem = Problem.read("good-mps-1.lp") diff --git a/docs/cuopt/source/images/aws1.png b/docs/cuopt/source/images/aws1.png deleted file mode 100644 index c48a117a4f..0000000000 Binary files a/docs/cuopt/source/images/aws1.png and /dev/null differ diff --git a/docs/cuopt/source/images/aws2.png b/docs/cuopt/source/images/aws2.png deleted file mode 100644 index 39fb380c66..0000000000 Binary files a/docs/cuopt/source/images/aws2.png and /dev/null differ diff --git a/docs/cuopt/source/images/aws3.png b/docs/cuopt/source/images/aws3.png deleted file mode 100644 index c9ab08d9e4..0000000000 Binary files a/docs/cuopt/source/images/aws3.png and /dev/null differ diff --git a/docs/cuopt/source/images/aws4.png b/docs/cuopt/source/images/aws4.png deleted file mode 100644 index 0028b90ff8..0000000000 Binary files a/docs/cuopt/source/images/aws4.png and /dev/null differ diff --git a/docs/cuopt/source/images/azure1.png b/docs/cuopt/source/images/azure1.png deleted file mode 100644 index c859aaede8..0000000000 Binary files a/docs/cuopt/source/images/azure1.png and /dev/null differ diff --git a/docs/cuopt/source/images/azure2.png b/docs/cuopt/source/images/azure2.png deleted file mode 100644 index 80f3fe55e0..0000000000 Binary files a/docs/cuopt/source/images/azure2.png and /dev/null differ diff --git a/docs/cuopt/source/images/azure3.png b/docs/cuopt/source/images/azure3.png deleted file mode 100644 index 1242ac2d2c..0000000000 Binary files a/docs/cuopt/source/images/azure3.png and /dev/null differ diff --git a/docs/cuopt/source/images/azure4.png b/docs/cuopt/source/images/azure4.png deleted file mode 100644 index 14e2029174..0000000000 Binary files a/docs/cuopt/source/images/azure4.png and /dev/null differ diff --git a/docs/cuopt/source/images/azure5.png b/docs/cuopt/source/images/azure5.png deleted file mode 100644 index 00fbb23bba..0000000000 Binary files a/docs/cuopt/source/images/azure5.png and /dev/null differ diff --git a/docs/cuopt/source/images/azure6.png b/docs/cuopt/source/images/azure6.png deleted file mode 100644 index 55ec667c82..0000000000 Binary files a/docs/cuopt/source/images/azure6.png and /dev/null differ diff --git a/docs/cuopt/source/images/cuOpt-self-hosted.png b/docs/cuopt/source/images/cuOpt-self-hosted.png deleted file mode 100644 index 9915c69b78..0000000000 Binary files a/docs/cuopt/source/images/cuOpt-self-hosted.png and /dev/null differ diff --git a/docs/cuopt/source/index.rst b/docs/cuopt/source/index.rst deleted file mode 100644 index 266aaf7b47..0000000000 --- a/docs/cuopt/source/index.rst +++ /dev/null @@ -1,105 +0,0 @@ -================ -NVIDIA cuOpt -================ - -.. toctree:: - :maxdepth: 4 - :caption: NVIDIA cuOpt - :name: NVIDIA cuOpt - - introduction.rst - install.rst - release-notes - - system-requirements.rst - -.. toctree:: - :maxdepth: 4 - :caption: Features - :name: Features - - routing-features.rst - convex-features.rst - milp-features.rst - -========================== -C (libcuopt) -========================== -.. toctree:: - :maxdepth: 4 - :caption: C API - :name: C API - - C Overview - -========================== -Python (cuopt) -========================== -.. toctree:: - :maxdepth: 4 - :caption: Python API - :name: Python API - - Python Overview - -==================================== -gRPC Remote Execution -==================================== -.. toctree:: - :maxdepth: 4 - :caption: gRPC Remote Execution - :name: gRPC Remote Execution - - gRPC Overview - -=============================== -Server (cuopt-server) -=============================== -.. toctree:: - :maxdepth: 1 - :caption: Server - :name: Server - - Server Overview - -================================== -Command Line Interface (cuopt-cli) -================================== -.. toctree:: - :maxdepth: 1 - :caption: Command Line Interface - :name: Command Line Interface - - Command Line Interface Overview - -======================================== -Third-Party Modeling Languages -======================================== -.. toctree:: - :maxdepth: 4 - :caption: Third-Party Modeling Languages - :name: Third-Party Modeling Languages - - thirdparty_modeling_languages/index.rst - -============= -Resources -============= -.. toctree:: - :maxdepth: 4 - :caption: Resources - :name: Resources - - faq.rst - transition.rst - resources.rst - -============= -Licenses -============= -.. toctree:: - :maxdepth: 4 - :caption: Licenses - :name: Licenses - - license.rst diff --git a/docs/cuopt/source/install.rst b/docs/cuopt/source/install.rst deleted file mode 100644 index 404d7361f8..0000000000 --- a/docs/cuopt/source/install.rst +++ /dev/null @@ -1,23 +0,0 @@ -============ -Installation -============ - -Choose your interface, install method, and options below to get the exact install command. Ensure your system meets the :doc:`system-requirements` before installing. - -Install Selector -================ - -.. install-selector:: - -Quick Start Guides -================== - -If the selector does not load or you prefer step-by-step guides, use the quick-start pages for each interface: - -* **Python (cuopt)** — :doc:`cuopt-python/quick-start` -* **C (libcuopt)** — :doc:`cuopt-c/quick-start` (includes ``cuopt_cli``) -* **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` - -See :doc:`system-requirements` for GPU, CUDA, driver, and OS requirements. diff --git a/docs/cuopt/source/introduction.rst b/docs/cuopt/source/introduction.rst deleted file mode 100644 index 1769a30bdf..0000000000 --- a/docs/cuopt/source/introduction.rst +++ /dev/null @@ -1,171 +0,0 @@ -========================== -Introduction -========================== - -**NVIDIA® cuOpt™** is a GPU-accelerated optimization library that solves `Linear Programming (LP) `_, `Quadratic Programming (QP) `_, and `Vehicle Routing Problems (VRP) `_, with support for `Quadratically Constrained Quadratic Programming (QCQP) `_ (beta), `Second-Order Cone Programming (SOCP) `_ (beta), and `Mixed Integer Linear Programming (MILP) `_ (beta). It enables solutions for large-scale problems with millions of variables and constraints, offering seamless deployment across hybrid and multi-cloud environments. - -Using accelerated computing, NVIDIA® cuOpt optimizes operations research and logistics by enabling better, faster decisions. - -As part of `NVIDIA AI Enterprise `_, NVIDIA cuOpt offers a secure, efficient way to rapidly generate world-class route optimization solutions. Using a single optimized container, you can deploy the AI microservice in under 5 minutes on accelerated NVIDIA GPU systems in the cloud, data center, workstations, or PCs. A license for NVIDIA AI Enterprise or membership in the NVIDIA Developer Program is required. For more information about NVAIE licensing, accessing NGC registry, and pulling container images, please refer to the :doc:`FAQ section `. - -.. note:: - NVAIE support is extended to only cuOpt Routing service API. LP and MILP are not supported as part of it, they are just add-ons. - -.. note:: - Check out this `FAQ `__ for more information about the NVIDIA Developer Program. - - -The core engine is built on C++ and all the APIs are built on top of it as wrappers. For example, cuOpt Python API uses Cython to wrap the C++ core engine and provide a Python interface. Similarly, other interfaces wrap different layers to communicate with the core engine. - -Routing (TSP, VRP, and PDP) -============================= - -The **Vehicle Routing Problem (VRP)** and **Pickup and Delivery Problems (PDP)** are derived from the **Traveling Salesperson Problem (TSP)**, which is one of the most studied problems in operations research and, more generally, in computer science. - -TSP asks the following question: - - - Given a list of destinations and a matrix of distances between each pair of destinations, what is the shortest possible route that visits each destination exactly one time and returns to the original location? - -For example, the TSP has several applications in planning and logistics, where a good solution can save significant travel time and fuel costs in the transportation and delivery of goods. VRP and PDP are essentially extensions of TSP with additional complexity. - -The VRP generalizes the TSP to solve for the optimal set of routes for a fleet of vehicles in order to deliver to a given set of customers. The PDP adds the possibility of two different types of services, namely pickup or delivery, whereas in VRP all customers require the same service be performed at a customer location. - - -How cuOpt Solves the Routing Problem -------------------------------------- - -cuOpt first generates an initial population of solutions, then iteratively improves the population until the time limit is reached, and picks the best solution from the population. - - -The Necessity for Heuristics ------------------------------- - -Given the time and computational resources required for brute-force enumeration, obtaining the exact optimal solution is not realistic. However, there are well-studied heuristics that yield near-optimal solutions for very large problems within a reasonable time, and NVIDIA cuOpt focuses on using these heuristics. - - - -Linear Programming (LP) -======================= - -**Linear Programming** is a technique for optimizing a linear objective function over a feasible region defined by a set of linear inequality and equality constraints. For example, consider the following system constraints - - 2x + 4y >= 230 - - 3x + 2y <= 190 - - x >= 0 - - y >= 0, - -and suppose we want to maximize the objective function - - f(x,y) = 5x + 3y. - -This is a linear program. - - -How cuOpt Solves the Linear Programming Problem ------------------------------------------------- -cuOpt includes three LP solving methods: - -* **PDLP**: Based on `PDLP `__, a First-Order Method (FOM) for solving large-scale LPs. This solver implements primal-dual hybrid gradient enhanced by heuristics. Sparse matrix-vector products are perfomed efficiently on NVIDIA GPUs. - -* **Barrier (Interior-Point)**: A primal-dual interior-point method that uses GPU-accelerated sparse Cholesky and LDLT solves via cuDSS, and sparse matrix operations via cuSparse. - -* **Dual Simplex**: A CPU-based dual simplex solver for small to medium-sized problems. - -All three algorithms can be run concurrently on both GPU and CPU, with the fastest solution returned automatically. - -Mixed Integer Linear Programming (MILP) (Beta) -============================================== - -A **Mixed Integer Program (MIP)** is an optimization problem where some variables are restricted to take on only integer values, while other variables can vary continuously. A **Mixed Integer Linear Program (MILP)** is a MIP with a linear objective and linear constraints. - -.. note:: - - The cuOpt MIP solver is in **beta** and under active development. The solver - currently excels at finding high-quality feasible solutions quickly with - GPU-accelerated primal heuristics. Proving feasible solutions optimal remains - under active development. - -For example, consider the following system of constraints: - - 2x + 4y >= 230 - - 3x + 2y <= 190 - - x >= 0 and x is integer - - y >= 0 and y is continuous, - -and suppose we wish to maximize the objective function - - f(x,y) = 5x + 3y. - -This is a mixed integer linear program. - -Although MILPs seems similar to a LPs, they require much more computation to solve. - -How cuOpt Solves the Mixed-Integer Linear Programming Problem -------------------------------------------------------------- - -cuOpt combines GPU-accelerated primal heuristics for improving the primal bound with traditional CPU algorithms, including branch and bound, to improve the dual bound. Primal heuristics such as local search, feasibility pump, and feasibility jump run on the GPU. Integer feasible solutions are shared between these components. - - -============================= -Supported APIs -============================= - -cuOpt supports the following APIs: - -- C API support - - :doc:`Linear Programming (LP) / Quadratic Programming (QP) - C ` - - :doc:`Mixed Integer Linear Programming (MILP) - C ` -- C++ API support - - cuOpt is written in C++ and includes a native C++ API. However, we do not provide documentation for the C++ API at this time. We anticipate that the C++ API will change significantly in the future. Use it at your own risk. -- Python support - - :doc:`Routing (TSP, VRP, and PDP) - Python ` - - :doc:`Linear Programming (LP) / Quadratic Programming (QP) and Mixed Integer Linear Programming (MILP) - Python ` -- gRPC remote execution - - :doc:`Linear Programming (LP) / Quadratic Programming (QP) and Mixed Integer Linear Programming (MILP) - gRPC remote ` -- Server support - - :doc:`Linear Programming (LP) - Server ` - - :doc:`Mixed Integer Linear Programming (MILP) - Server ` - - :doc:`Routing (TSP, VRP, and PDP) - Server ` -- Third-party modeling languages - - `AMPL `_ - - `GAMS `_ - - `PuLP `_ - - `JuMP `_ - - `Pyomo `_ - - `CVXPY `_ - - -================================== -Installation Options -================================== - -NVIDIA cuOpt is available in several formats to suit different deployment needs: - -Source Code -=========== -For users who want to customize cuOpt or contribute to its development, the source code is available on `GitHub `_. Building from source allows maximum flexibility but requires setting up the build environment. - -Pip Wheels -========== -For Python users with existing pip-based workflows, cuOpt can be installed directly via pip from the NVIDIA Python Package Index. This is the simplest installation method for most users. - -Conda Packages -=============== -Available from the NVIDIA channel, conda packages provide a convenient way to manage cuOpt and its dependencies in conda environments. This is ideal for users who prefer conda-based workflow management. - -Containers -=========== -NVIDIA provides ready-to-use containers with cuOpt pre-installed, available from: - -- Docker Hub (``nvidia/cuopt``) -- NVIDIA NGC (for NVIDIA AI Enterprise subscribers) - -Containers offer a consistent, isolated environment and are particularly useful for cloud deployments or microservices architectures. - -For detailed installation instructions for each option, please refer to the respective quickstart guides in the documentation. diff --git a/docs/cuopt/source/license.rst b/docs/cuopt/source/license.rst deleted file mode 100644 index 2e7b99eb49..0000000000 --- a/docs/cuopt/source/license.rst +++ /dev/null @@ -1,6 +0,0 @@ -================================= -cuOpt License -================================= - -.. literalinclude:: ../../../LICENSE - :language: text diff --git a/docs/cuopt/source/milp-features.rst b/docs/cuopt/source/milp-features.rst deleted file mode 100644 index 4be820fef2..0000000000 --- a/docs/cuopt/source/milp-features.rst +++ /dev/null @@ -1,92 +0,0 @@ -==================== -MILP Features -==================== - -Availability ------------- - -The cuOpt MIP solver is in **beta** and under active development. The solver -currently excels at finding high-quality feasible solutions quickly with -GPU-accelerated primal heuristics. Proving feasible solutions optimal remains -under active development. - -The MILP solver can be accessed in the following ways: - -- **Third-Party Modeling Languages**: cuOpt's LP and MILP solver can be called directly from the following third-party modeling languages. This allows you to leverage GPU acceleration while maintaining your existing optimization workflow in these modeling languages. - - Currently supported solvers: - - AMPL - - GAMS - - PuLP - - JuMP - - Pyomo - -- **C API**: A native C API that provides direct low-level access to cuOpt's MILP solver, enabling integration into any application or system that can interface with C. - -- **Python SDK**: A Python package that provides direct access to cuOpt's MILP 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`. - -- **As a Self-Hosted Service**: cuOpt's MILP 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. - -Variable Bounds ---------------- - -Lower and upper bounds can be applied to each variable. If no variable bounds are specified, the default bounds are ``[-inf,+inf]``. - -Constraints ------------ - -The constraint matrix is specified in `Compressed Sparse Row (CSR) format `_. - -There are two ways to specify constraints in cuOpt MILP: - -1. Using row_type and right-hand side: - - Constraints can be specified in the form: - - A*x {<=, =, >=} b - - where A is the constraint matrix in CSR format, x is the variable vector, and b is the right-hand side vector. The relationship {<=, =, >=} is specified through the ``row_type`` parameter. - -2. Using constraint bounds: - - Alternatively, constraints can be specified as two-sided inequalities: - - lb <= A*x <= ub - - where lb and ub are vectors of lower and upper bounds respectively. This form allows specifying both bounds of a single constraint. - -Both forms are mathematically equivalent. The choice between them is a matter of convenience depending on your problem formulation. - -Incumbent Solution Callback in the Service ------------------------------------------- - -When using the service, users can provide a callback to receive new integer feasible solutions that improve the objective (called incumbents) while the solver is running. An :ref:`Incumbent Example ` is shared on the self-hosted page. - -Logging -------- - -The CUOPT_LOG_FILE parameter can be set to write detailed solver logs for MILP problems. This parameter is available in all APIs that allow setting solver parameters except for the cuOpt service. For the service, see the logging callback below. - -Logging Callback in the Service -------------------------------- - -In the cuOpt service API, the ``log_file`` value in ``solver_configs`` is ignored. - -If however you set the ``solver_logs`` flag on the ``/cuopt/request`` REST API call, users can fetch the log file content from the webserver at ``/cuopt/logs/{id}``. Using the logging callback feature through the cuOpt client is shown in :ref:`Logging Callback Example ` on the self-hosted page. - - -Time Limit --------------- - -The user may specify a time limit to the solver. By default the solver runs until a solution is found or the problem is determined to be infeasible or unbounded. - -.. note:: - - Note that ``time_limit`` applies only to solve time inside the LP solver. This does not include time for network transfer, validation of input, and other operations that occur outside the solver. The overhead associated with these operations are usually small compared to the solve time. - -Node Limit --------------- - -The user may specify a maximum number of branch-and-bound nodes via ``node_limit``. The solver stops once this limit is reached and returns the current best feasible solution, if any. If both ``time_limit`` and ``node_limit`` are set, the solver stops at whichever limit is hit first. By default there is no node limit. diff --git a/docs/cuopt/source/mip-settings.rst b/docs/cuopt/source/mip-settings.rst deleted file mode 100644 index 025370ea3c..0000000000 --- a/docs/cuopt/source/mip-settings.rst +++ /dev/null @@ -1,384 +0,0 @@ -============ -MIP Settings -============ - - -This page describes the parameter settings available for cuOpt's MIP solver. These parameters are set as :ref:`parameter constants ` in case of C API and in case of Server Thin client as raw strings. Refer to examples in :doc:`C ` and :doc:`Server Thin client ` for more details. - -.. note:: - When setting parameters in thin client solver settings, remove ``CUOPT_`` from the parameter name and convert to lowercase. For example, ``CUOPT_TIME_LIMIT`` would be set as ``time_limit``. - -Common Parameters ------------------ - -We begin by describing parameters common to both the MILP and LP solvers. - - -Time Limit -^^^^^^^^^^ -``CUOPT_TIME_LIMIT`` controls the time limit in seconds after which the solver will stop and return the current solution. -For performance reasons, cuOpt does not constantly checks for time limit. Thus, the solver -may run slightly over the limit. If set along with the iteration limit, cuOpt will stop when -the first limit (iteration or time) is hit. - - -.. note:: By default there is no time limit. So cuOpt will run until it finds an optimal solution, - or proves the problem is infeasible or unbounded. - - - -Log to Console -^^^^^^^^^^^^^^ -``CUOPT_LOG_TO_CONSOLE`` controls whether cuOpt should log information to the console during a solve. -If true, a logging info is written to the console; if false no logging info is written to the console (logs may still be written to a file.) - -.. note:: The default value is true. - -Log File -^^^^^^^^ -``CUOPT_LOG_FILE`` controls the name of a log file where cuOpt should write information about the solve. - -.. note:: The default value is ``""`` and no log file is written. This setting is ignored by the cuOpt service, use the log callback feature instead. - -Solution File -^^^^^^^^^^^^^ -``CUOPT_SOLUTION_FILE`` controls the name of a file where cuOpt should write the solution. - -.. note:: The default value is ``""`` and no solution file is written. This setting is ignored by the cuOpt service. - -User Problem File -^^^^^^^^^^^^^^^^^ -``CUOPT_USER_PROBLEM_FILE`` controls the name of a file where cuOpt should write the user problem. - -.. note:: The default value is ``""`` and no user problem file is written. This setting is ignored by the cuOpt service. - -Num CPU Threads -^^^^^^^^^^^^^^^ -``CUOPT_NUM_CPU_THREADS`` controls the number of CPU threads used in the MIP solver. Set this to a small value to limit -the amount of CPU resources cuOpt uses. Set this to a large value to improve solve times for CPU -parallel parts of the solvers. - -.. note:: By default the number of CPU threads is automatically determined based on the number of CPU cores. - -Presolve -^^^^^^^^ -``CUOPT_PRESOLVE`` controls whether to apply presolve reductions. Set this to 0 to disable presolve. - -.. note:: By default, presolve is enabled. - -Probing -^^^^^^^ -``CUOPT_MIP_PROBING`` toggles the probing-cache step of cuOpt's internal MIP presolve. The probing pass evaluates variable fixings to discover implications used later by branch-and-bound and the rounding heuristics. It is enabled by default (``true``); set it to ``false`` to skip probing while leaving the rest of presolve in place. Setting ``CUOPT_PRESOLVE=0`` already turns off the entire presolve pipeline, so ``CUOPT_MIP_PROBING`` only matters when presolve is otherwise enabled. Probing is also skipped in deterministic mode and on LP-only solves. - -Dual Postsolve -^^^^^^^^^^^^^^ -``CUOPT_DUAL_POSTSOLVE`` controls whether dual postsolve is enabled when using Papilo presolver for LP problems. Disabling dual postsolve can improve solve time at the expense of not having -access to the dual solution. Enabled by default for LP when Papilo presolve is selected. This is not relevant for MIP problems. - -Mixed Integer Linear Programming ---------------------------------- - -The cuOpt MIP solver is in **beta** and under active development. The solver -currently excels at finding high-quality feasible solutions quickly with -GPU-accelerated primal heuristics. Proving feasible solutions optimal remains -under active development. - -We now describe parameter settings for the MILP solver. - - -Heuristics Only -^^^^^^^^^^^^^^^ - -``CUOPT_MIP_HEURISTICS_ONLY`` controls if only the GPU heuristics should be run for the MIP problem. When set to true, only the primal -bound is improved via the GPU. When set to false, both the GPU and CPU are used and -the dual bound is improved on the CPU. - -.. note:: The default value is false. - -Scaling -^^^^^^^ - -``CUOPT_MIP_SCALING`` controls if scaling should be applied to the MIP problem. - -* ``0``: Scaling is off. -* ``1``: Scaling is on. -* ``2``: Scaling is not applied to the objective (default). - -Absolute Tolerance -^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_ABSOLUTE_TOLERANCE`` controls the MIP absolute tolerance. - -.. note:: The default value is ``1e-6``. - -Relative Tolerance -^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_RELATIVE_TOLERANCE`` controls the MIP relative tolerance. - -.. note:: The default value is ``1e-12``. - - -Integrality Tolerance -^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_INTEGRALITY_TOLERANCE`` controls the MIP integrality tolerance. A variable is considered to be integral, if -it is within the integrality tolerance of an integer. - -.. note:: The default value is ``1e-5``. - -Absolute MIP Gap -^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_ABSOLUTE_GAP`` controls the absolute tolerance used to terminate the MIP solve. The solve terminates when:: - - Best Objective - Dual Bound <= absolute tolerance - -when minimizing or - - Dual Bound - Best Objective <= absolute tolerance - -when maximizing. - -.. note:: The default value is ``1e-10``. - -Relative MIP Gap -^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_RELATIVE_GAP`` controls the relative tolerance used to terminate the MIP solve. The solve terminates when:: - - abs(Best Objective - Dual Bound) / abs(Best Objective) <= relative tolerance - -If the Best Objective and the Dual Bound are both zero the gap is zero. If the best objective value is zero, the -gap is infinity. - -.. note:: The default value is ``1e-4``. - - -Node Limit -^^^^^^^^^^ - -``CUOPT_NODE_LIMIT`` controls the maximum number of branch-and-bound nodes the MILP solver will explore before stopping and returning the current best feasible solution (if any). If set along with the time limit, cuOpt stops at whichever limit is hit first. - -.. note:: By default there is no node limit. - -Cut Passes -^^^^^^^^^^ - -``CUOPT_MIP_CUT_PASSES`` controls the maximum number of cut passes to run. Set this value to 0 to disable cuts. Set this value to larger numbers to perform more cut passes. - -.. note:: The default value is ``10``. - -Mixed Integer Rounding Cuts -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS`` controls whether to use mixed integer rounding cuts. -The default value of ``-1`` (automatic) means that the solver will decide whether to use mixed integer rounding cuts based on the problem characteristics. -Set this value to 1 to enable mixed integer rounding cuts. -Set this value to 0 to disable mixed integer rounding cuts. - -.. note:: The default value is ``-1`` (automatic). - -Mixed Integer Gomory Cuts -^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS`` controls whether to use mixed integer Gomory cuts. -The default value of ``-1`` (automatic) means that the solver will decide whether to use mixed integer Gomory cuts based on the problem characteristics. -Set this value to 1 to enable mixed integer Gomory cuts. -Set this value to 0 to disable mixed integer Gomory cuts. - -.. note:: The default value is ``-1`` (automatic). - -Strong Chvatal-Gomory Cuts -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS`` controls whether to use strong Chvatal-Gomory cuts. -The default value of ``-1`` (automatic) means that the solver will decide whether to use strong Chvatal-Gomory cuts based on the problem characteristics. -Set this value to 1 to enable strong Chvatal Gomory cuts. -Set this value to 0 to disable strong Chvatal Gomory cuts. - -.. note:: The default value is ``-1`` (automatic). - -Knapsack Cuts -^^^^^^^^^^^^^ - -``CUOPT_MIP_KNAPSACK_CUTS`` controls whether to use knapsack cuts. -The default value of ``-1`` (automatic) means that the solver will decide whether to use knapsack cuts based on the problem characteristics. -Set this value to 1 to enable knapsack cuts. -Set this value to 0 to disable knapsack cuts. - -.. note:: The default value is ``-1`` (automatic). - - -Cut Change Threshold -^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_CUT_CHANGE_THRESHOLD`` controls the threshold for the improvement in the dual bound per cut pass. -Larger values require the dual bound to improve significantly in each cut pass. -Set this value to -1 to allow the cut passes to continue even if the dual bound does not improve. - -.. note:: The default value is ``-1`` (no threshold). - -Cut Min Orthogonality -^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_CUT_MIN_ORTHOGONALITY`` controls the minimum orthogonality required for a cut to be added to the LP relaxation. -Set this value close to 1, to require all cuts be close to orthogonal to each other. -Set this value close to zero to allow more cuts to be added to the LP relaxation. - -.. note:: The default value is ``0.5``. - -Reduced Cost Strengthening -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_REDUCED_COST_STRENGTHENING`` controls whether to use reduced-cost strengthening. -When enabled, the solver will use integer feasible solutions to strengthen the bounds of integer variables. -The default value of ``-1`` (automatic) means that the solver will decide whether to use reduced-cost strengthening based on the problem characteristics. -Set this value to 0 to disable reduced-cost strengthening. -Set this value to 1 to perform reduced-cost strengthening during the root cut passes. -Set this value to 2 to perform reduced-cost strengthening during the root cut passes and after strong branching. - -.. note:: The default value is ``-1`` (automatic). - -Reliability Branching -^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_RELIABILITY_BRANCHING`` controls the reliability branching mode. -The default value of ``-1`` (automatic) means that the solver will decide whether to use reliability branching, and the reliability branching factor, based on the problem characteristics. -Set this value to 0 to disable reliability branching. -Set this value to k > 0, to enable reliability branching. A variable will be considered reliable if it has been branched on k times. - -.. note:: The default value is ``-1`` (automatic). - -Batch PDLP Strong Branching -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING`` controls whether to use batched PDLP over Dual Simplex during strong branching at the root. -When enabled, the solver evaluates multiple branching candidates simultaneously in a single batched PDLP solve rather than solving them in parallel using Dual Simplex. This can significantly reduce the time spent in strong branching if Dual Simplex is struggling. -Set this value to 0 to disable batched PDLP strong branching. -Set this value to 1 to enable batched PDLP strong branching. - -.. note:: The default value is ``0`` (disabled). This setting is ignored if the problem is not a MIP problem. - -Batch PDLP Reliability Branching -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_BATCH_PDLP_RELIABILITY_BRANCHING`` controls whether to use batched PDLP for reliability branching evaluations. -When enabled, candidate variables for reliability branching are evaluated simultaneously using a single batched PDLP solve. -Set this value to 0 to disable. -Set this value to 1 to enable. - -.. note:: The default value is ``0`` (disabled). - -Strong Branching Simplex Iteration Limit -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITERATION_LIMIT`` controls the maximum number of simplex iterations allowed per candidate during strong branching. -Reducing this value speeds up strong branching at the cost of less accurate candidate evaluations. - -.. note:: The default value is ``-1`` (choose the iteration limit automatically). - -MIP Determinism Mode -^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_DETERMINISM_MODE`` controls whether the MIP solver runs in opportunistic or deterministic mode. - -* ``0`` (``CUOPT_MODE_OPPORTUNISTIC``): Opportunistic mode — results may vary between runs due to parallelism (default). -* ``1`` (``CUOPT_MODE_DETERMINISTIC``): Deterministic mode — improves reproducibility across runs with the same number of threads. - -.. note:: The default value is ``0`` (opportunistic). - -.. warning:: Deterministic mode is experimental. It improves reproducibility in many cases but does not yet guarantee fully deterministic results in all scenarios. - -MIP Symmetry -^^^^^^^^^^^^^ - -``CUOPT_MIP_SYMMETRY`` controls symmetry detection and handling in the MIP solver. - -* ``-1``: Automatic (default) — cuOpt decides based on problem characteristics. -* ``0``: Disable symmetry handling. -* ``1``: Enable symmetry handling (orbital fixing only). -* ``2``: Enable symmetry handling (orbital fixing + lexical reduction). - -.. note:: The default value is ``-1`` (automatic). - -Flow Cover Cuts -^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_FLOW_COVER_CUTS`` controls whether to use flow cover cuts. -Set this value to ``-1`` (automatic) to let the solver decide. -Set this value to ``0`` to disable flow cover cuts. -Set this value to ``1`` to enable flow cover cuts. - -.. note:: The default value is ``-1`` (automatic). - -Implied Bound Cuts -^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_IMPLIED_BOUND_CUTS`` controls whether to use implied bound cuts. -Set this value to ``-1`` (automatic) to let the solver decide. -Set this value to ``0`` to disable implied bound cuts. -Set this value to ``1`` to enable implied bound cuts. - -.. note:: The default value is ``-1`` (automatic). - -Clique Cuts -^^^^^^^^^^^^ - -``CUOPT_MIP_CLIQUE_CUTS`` controls whether to use clique cuts. -Set this value to ``-1`` (automatic) to let the solver decide. -Set this value to ``0`` to disable clique cuts. -Set this value to ``1`` to enable clique cuts. - -.. note:: The default value is ``-1`` (automatic). - -Zero-Half Cuts -^^^^^^^^^^^^^^ - -``CUOPT_MIP_ZERO_HALF_CUTS`` controls whether to use zero-half cuts. -Set this value to ``-1`` (automatic) to let the solver decide. -Set this value to ``0`` to disable zero-half cuts. -Set this value to ``1`` to enable zero-half cuts. - -.. note:: The default value is ``-1`` (automatic). - -Objective Step -^^^^^^^^^^^^^^^ - -``CUOPT_MIP_OBJECTIVE_STEP`` controls whether cuOpt automatically detects and exploits discrete step structure in the objective to tighten the dual bound. - -* ``0``: Disable objective step detection. -* ``1``: Enable objective step detection (default). - -.. note:: The default value is ``1`` (enabled). - -Semi-Continuous Big-M -^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_MIP_SEMICONTINUOUS_BIG_M`` controls the Big-M coefficient used when linearizing semi-continuous variable constraints. -A semi-continuous variable is either zero or takes a value in the range ``[lower_bound, upper_bound]``. -Set this to a positive value that is at least as large as the upper bound of any semi-continuous variable in the problem. - -.. note:: By default cuOpt derives the Big-M from the variable's upper bound. - -Work Limit -^^^^^^^^^^ - -``CUOPT_WORK_LIMIT`` controls the work limit after which the solver will stop and return the current solution. -Work units are a machine-independent measure of solver effort. If set along with the time limit or iteration limit, cuOpt will stop when the first limit is hit. - -.. note:: By default there is no work limit. - -Random Seed -^^^^^^^^^^^^ - -``CUOPT_RANDOM_SEED`` controls the random seed used by the solver. Setting a fixed seed enables reproducible results when running in deterministic mode. - -.. note:: By default the random seed is set automatically. - -Presolve File -^^^^^^^^^^^^^^ - -``CUOPT_PRESOLVE_FILE`` controls the name of a file where cuOpt should write presolve information. - -.. note:: The default value is ``""`` and no presolve file is written. diff --git a/docs/cuopt/source/open-api.rst b/docs/cuopt/source/open-api.rst deleted file mode 100644 index 0827a6fe26..0000000000 --- a/docs/cuopt/source/open-api.rst +++ /dev/null @@ -1,6 +0,0 @@ -======================================== -cuOpt Open-API Reference - Swagger -======================================== - -.. swagger-plugin:: cuopt_spec.yaml - :id: cuopt-api diff --git a/docs/cuopt/source/release-notes.md b/docs/cuopt/source/release-notes.md deleted file mode 120000 index dbb6e2029d..0000000000 --- a/docs/cuopt/source/release-notes.md +++ /dev/null @@ -1 +0,0 @@ -../../../RELEASE-NOTES.md \ No newline at end of file diff --git a/docs/cuopt/source/resources.rst b/docs/cuopt/source/resources.rst deleted file mode 100644 index eebe6722dc..0000000000 --- a/docs/cuopt/source/resources.rst +++ /dev/null @@ -1,37 +0,0 @@ -===================== -Resources -===================== - - -`Sample Notebooks `_ ----------------------------------------------------------------------------------- - - -cuOpt Examples and Tutorials Videos ------------------------------------ - -.. dropdown:: Tutorial List - - - `Quick Start to GPU-Accelerated Large-Scale Logistics and Supply Chain Optimization with NVIDIA cuOpt `_ - - `Accelerated MILP for Supply Chain, Logistics & Planning Optimization — Quick Start with NVIDIA cuOpt `_ - - `Solving Vehicle Routing Problems — Hands-On with Open Source NVIDIA cuOpt `_ - - -`Test cuOpt with NVIDIA Launchable `_ ------------------------------------------------------------------------------------------------------------------------------- - -`Test cuOpt on Google Colab `_ ------------------------------------------------------------------------------------------------------------------------- -Please note that you need to choose a `Runtime` as `GPU` in order to run the notebooks. - -`File a Bug `_ ---------------------------------------------------------------------------------- - -`Ask a Question `_ ------------------------------------------------------------------------------------------- - -`Blogs `_ ----------------------------------------------------------------------------- - -Contact us - cuopt@nvidia.com ------------------------------ diff --git a/docs/cuopt/source/routing-features.rst b/docs/cuopt/source/routing-features.rst deleted file mode 100644 index 7442bbd213..0000000000 --- a/docs/cuopt/source/routing-features.rst +++ /dev/null @@ -1,126 +0,0 @@ -======================== -Routing Features -======================== - -Availability ------------------------------- - -The Routing solver is available in two forms: - -- **As a Self-Hosted Service**: cuOpt Routing can be deployed as a service in your own infrastructure, enabling you to maintain full control while integrating it into your existing systems. For more information, see :doc:`cuopt-server/quick-start`. - -- **Python SDK**: A Python package that provides direct access to cuOpt's routing 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`. - -Both options provide the same routing optimization capabilities while offering flexibility in deployment and integration approaches. - -Batch Solving (TSP) ------------------------------- - -The Python API supports solving multiple TSP (or routing) instances in a single call via **batch mode**. Use :func:`cuopt.routing.BatchSolve` with a list of :class:`cuopt.routing.DataModel` objects to run many problems in parallel and improve throughput. See :doc:`cuopt-python/routing/routing-examples` for details and an example. - -Heterogeneous Fleet ------------------------------- - -In a Vehicle Routing Problem (VRP), enterprise customers may have a fleet that is composed of different vehicles, such as trucks and motorbikes. Each vehicle type will likely have different costs, travel times, different constraints such as time windows, start and end locations, and carrying capacity. To model such scenarios, users can provide multiple input matrices (travel time matrix/cost), one for each type. Constraints must be provided for each vehicle. - -Multiple Input Matrices ------------------------------- - -A cost matrix needs to be a square matrix of some travel metric that is passed to the NVIDIA cuOpt solver. In many variations of the Traveling Salesman Problem (TSP), this includes travel time, distance, or another business cost. A cost matrix is a square matrix that represents the cost of traveling between each two pairs of locations in the problem. ``cost matrix`` can hold any cost matrix; the cost can be travel time, travel distance, dollar cost, or any weighted function of cost. If the cost matrix doesn't use travel time, ``travel time matrix`` should be used so that the cost matrix is used for optimization, while the travel time matrix is used for meeting time constraints. - -In the case of a heterogeneous fleet, different vehicle types may have different travel times. To support this, multiple ``cost matrix`` and ``travel time matrix`` can be provided such that each vehicle type has a corresponding matrix. - -Multiple Input Waypoint Graphs ------------------------------- - -Similar to cost matrix, distance and time details can be provided through waypoint graphs through ``cost waypoint graph`` or ``travel time waypoint graph``. Each vehicle type will have its own waypoint graph data for both cost and time. - -Vehicle Time Windows ------------------------------- - -Time windows represent the operating time of the vehicles. There is one time window per vehicle. This data is represented as an integer. Raw data may include Universal Time Stamp (UTC) date/time format or string format which must be converted to floating value. (Example: 9:00 am - 6:00 pm converted to minutes in a 24-hour period starting at 12:00 am, would be ``[540, 1080]``). All time/cost units provided to the cuOpt solver should be in the same unit. If the travel time cost matrix includes the travel time in between locations in minutes, the time windows must be integer representation of minutes as well. - -.. note:: - - All time windows are expected to be => 0, and all time windows are int32 types. So the time window range supported would be ``[0, 2^31-1]``; this translates to ``[January 1, 1970, 00:00:00, January 19, 2038, 03:14:07]`` in UTC. - - -Vehicle Breaks ------------------------------- - -In addition to vehicle time windows, vehicles may also have break time windows. If the time windows represent a driver's working hours in a day, the break time window may represent their lunch break in the middle of the day. The integer representation is consistent with the time windows. In case raw input data is in UTC timestamp, if a vehicle is working a shift of 9:00 am - 6:00 pm, then in a 24-hour period this is equivalent to ``540 - 1080``. If the vehicle has a break from 1:00 pm - 1:30 pm, then the break time window would be ``[780, 810]``. - -Along with time, users can also specify possible break locations using ``vehicle break locations``. - -There are two types of breaks, - -- Uniform breaks - Where each driver in the fleet has same number breaks that they can avail. Like 3 breaks in a shift (coffee, lunch, coffee). This is achieved through ``vehicle break time windows`` and ``vehicle break durations``. - -- Non-Uniform breaks - Where each driver might need to take different set of breaks, may be someone called-in sick or some set of workers need more breaks since they work extra hours. This is done through ``vehicle breaks``. - -Only one of the type of breaks can be used at a time. - - -Prize Collection ------------------------- -The *prizes* may be used to set a prize for each task. This supports cases where all tasks are not feasible or where-in servicing all tasks are not as profitable as dropping some low prize tasks and servicing the rest and that the user requires tasks with the highest prize to be considered before trying other tasks. If the cost of serving a customer is too high compared to the prize of serving a customer, the solver will drop that customer to improve overall cost. - -If the primary goal is to serve as many customers as possible, it is advisable to set very high prizes. Prize collection would be an effective way to drop infeasible tasks and prioritizing tasks with higher prizes. The main difference is in objective function, the total prize will be negated rather than getting added to overall cost. If the objective weight corresponding to prize objective is set to zero, the prize collection mechanism is disabled. - -Objectives ------------------------------- - -The default objective of the solver is to optimize the cost computed using cost matrices. However, a few other objectives can be set. The final objective is the linear combination of all the objectives specified. The specified objective weights are used as coefficients. By default, the cuOpt solver optimizes for vehicle count first and final objective next. - -Drop First / Last Trips ------------------------------- - -By dropping the first or last trip, the cuOpt solver does not take into account the vehicles' trip from their depots to their first stop, or the trip from their last stop back to the depots. With these parameters, the route includes only travel costs and times between task locations. In cases where drivers may start their shift from their home location instead of an assigned depot, the trip to and from the depot is unnecessary. - -Pickup and Deliveries ------------------------------- - -Some use cases may include picking up an order from one location and delivering it to another. Each order has two corresponding locations: one for pickup and one for delivery. The same vehicle must handle both the pickup and delivery of the same order, and the pickup of the order must occur prior to the delivery. - -Precise Time Limits ------------------------------- - -It is recommended to use the default time limit (which is ``10 + number_of_nodes/6``) seconds. If the time limit is set to X seconds, then solver continues to run for X seconds even when there is no improvement in the solution quality. - -.. note:: - - In case of self-hosted version, the ``time_limit`` set is what solver will essentially use to solve the problem and doesn't include ``network transfer``, ``etl``, ``validation of input``, ``instance being busy with other requests`` and few other overheads, these overheads would be comparatively smaller. So the overall request to response round trip time would be ``solve_time`` + ``overhead``. - -Vehicle Start and End Locations ------------------------------------------ - -Each vehicle in the fleet must have a start and end location for the given set of locations in a waypoint graph or cost matrix. These locations must be included in the cost matrices or waypoint graphs. In many use cases, these start and end locations are depots or distribution centers, such that vehicles depart their assigned depot in the morning, fulfill all of their assigned tasks, and then return to the assigned depot at the end of the work day. The start and end locations are not necessarily the same location (e.g., a vehicle departs from depot 1 in the morning but returns to depot 2 at night). - -Minimum Constraint on Number of Vehicles ------------------------------------------ - -By default, cuOpt tries to minimize the number of vehicles used in the solution and considers the fleet size an upper bound. If a given input fleet has 20 vehicles available but only 10 are needed to fulfill all of the tasks, then the cuOpt solution will include only 10 vehicles. To set a lower bound on the number of vehicles used, set ``min_vehicles`` in ``fleet_data``. If the exact number of vehicles to be used is known, specifying a fleet of the desired size and setting ``min vehicles`` equal to the fleet size will guarantee that all vehicles are used. - -Maximum Constraints per Vehicle ------------------------------------------ - -Vehicles may have a constraint for maximum distance each vehicle can travel or maximum time a vehicle can operate. This means that even if a vehicle has a time window of 9am to 9pm, and a driver may be available to work for those 12 hours, we can add a constraint that a work day must not exceed 8 of those 12 hours. - -Fixed Cost per Vehicle ------------------------ -Vehicles can have different fixed costs associated with them. This helps in scenarios where a single vehicle with a higher cost can be avoided if it can be done with two or more vehicles with lesser costs. This would be dependent on the objective function. - -Mapping Orders to Vehicles, and Vehicles to Orders ---------------------------------------------------- -By default, cuOpt will assign orders to vehicles based on the optimal routes. However, in some cases, it makes sense to assign specific orders to specific vehicles, or, conversely, specific vehicles to specific orders. - -- ``order vehicle match`` allows assigning orders to vehicles. For example, a food distribution center wants to make shipments to grocery stores around the city. Say the fleet consists of refrigerated trucks, such that they can carry frozen food, and vans, which cannot. In this case, we want to assign the orders that contain frozen food to the trucks (rather than just any vehicle). - -- ``vehicle order match`` allows assigning vehicles to orders. For example, a maintenance company can have many employees (technicians) who can fulfil various tasks. When a customer requests a service, the company may dispatch any available technician to fulfill their request. However, if a customers request a service that only one technician can fulfill, those orders can be assigned to this one technician. - -In cases where a set of orders need to be assigned to a set of vehicles, either constraint can be used as long as the mapping is done correctly. - -Initial Solution ----------------- - -Previously run solutions or uploaded solutions can be used as an initial solution to start a new request to boost the speed to the solution. :ref:`Examples ` are shared on the self-hosted page. diff --git a/docs/cuopt/source/system-requirements.rst b/docs/cuopt/source/system-requirements.rst deleted file mode 100644 index 26dcf84b22..0000000000 --- a/docs/cuopt/source/system-requirements.rst +++ /dev/null @@ -1,107 +0,0 @@ -=================== -System Requirements -=================== - -Dependencies are installed automatically when using the pip and Conda installation methods. However, users would still need to make sure the system meets the minimum requirements. - -.. dropdown:: Minimum Requirements - - * System Architecture: - - x86-64 - - ARM64 - - * GPU: - - Volta architecture or better (Compute Capability >=7.0) - - * CPU: - - 4+ cores - - * System Memory: - - 16+ GB RAM - - * NVMe SSD Storage: - - 100+ GB free space - - * CUDA: - - 12.0+ - - * Python: - - >= 3.11.* and <= 3.14.* - - * OpenSSL: - - 3.x runtime (``libssl.so.3`` and ``libcrypto.so.3``) must be present on the host - - Shipped by default on Ubuntu 22.04+, Debian 12+, RHEL/Rocky/Alma 9+, Fedora 36+ - - On RHEL/Rocky/Alma 8, install the ``openssl3`` package from EPEL (see :doc:`faq`) - - On Ubuntu 20.04, install OpenSSL 3 from a PPA/backport or use the cuOpt container - - * NVIDIA drivers: - - 525.60.13+ (Linux) - - 527.41+ (Windows) - - * OS: - - Linux distributions with glibc>=2.28 (released in August 2018): - * Arch Linux (minimum version 2018-08-02) - * Debian (minimum version 10.0) - * Fedora (minimum version 29) - * Linux Mint (minimum version 20) - * Rocky Linux / Alma Linux / RHEL (minimum version 8) - * Ubuntu (minimum version 20.04) - * Windows 11 with WSL2 - - * CUDA & NVIDIA Driver combinations: - - CUDA 12.0 with Driver 525.60.13+ - - CUDA 12.2 with Driver 535.86.10+ - - CUDA 12.5 with Driver 555.42.06+ - - CUDA 12.9 with Driver 570.42.01+ - - CUDA 13.0 with Driver 580.65.06+ - -.. dropdown:: Recommended Requirements for Best Performance - - * System Architecture: - - x86-64 - - ARM64 - - * GPU: - - NVIDIA H100 SXM (compute capability >= 9.0) - - * CPU: - - 32+ cores - - * System Memory: - - 64+ GB RAM - - * NVMe SSD Storage: - - 100+ GB free space - - * CUDA: - - 13.0 - - * Latest NVIDIA drivers (580.65.06+) - - * OS: - - Linux distributions with glibc>=2.28 (released in August 2018): - * Arch Linux (minimum version 2018-08-02) - * Debian (minimum version 10.0) - * Fedora (minimum version 29) - * Linux Mint (minimum version 20) - * Rocky Linux / Alma Linux / RHEL (minimum version 8) - -The above configuration will provide optimal performance for large-scale optimization problems. - - -Container ---------- - -* `nvidia-container-toolkit `_ needs to be installed - - -Thin-client for Self-Hosted ----------------------------- - -* OS: Linux - -* System Architecture: - - x86-64 - - ARM64 - -* Python >= 3.11.x <= 3.14.x diff --git a/docs/cuopt/source/thirdparty_modeling_languages/index.rst b/docs/cuopt/source/thirdparty_modeling_languages/index.rst deleted file mode 100644 index 040ec8db8f..0000000000 --- a/docs/cuopt/source/thirdparty_modeling_languages/index.rst +++ /dev/null @@ -1,42 +0,0 @@ -=============================== -Third-Party Modeling Languages -=============================== - - --------------------------- -AMPL Support --------------------------- - -AMPL can be used with near zero code changes: simply switch to cuOpt as a solver to solve linear and mixed-integer programming problems. Please refer to the `AMPL documentation `_ for more information. Also, see the example notebook in the `colab `_. - --------------------------- -GAMS and GAMSPy Support --------------------------- - -GAMS and GAMSPy models can be used with near zero code changes after setting up the solver link: simply switch to cuOpt as a solver to solve linear and mixed-integer programming problems (e.g. ``gams trnsport lp=cuopt``). Please refer to the `GAMS cuOpt link repository `_ for more information on how to setup GAMS and GAMSPy for cuOpt. Also, see the example notebook in the `cuopt-examples `_ repository. - --------------------------- -PuLP Support --------------------------- - -PuLP can be used with near zero code changes: simply switch to cuOpt as a solver to solve linear and mixed-integer programming problems. -Please refer to the `PuLP documentation `_ for more information. Also, see the example notebook in the `cuopt-examples `_ repository. - --------------------------- -JuMP Support --------------------------- - -JuMP can be used with near zero code changes: simply switch to cuOpt as a solver to solve linear and mixed-integer programming problems. -Please refer to the `JuMP documentation `_ for more information. - --------------------------- -Pyomo Support --------------------------- - -Pyomo models can be used with near zero code changes via cuOpt's direct solver interface: simply select cuOpt as the solver to solve linear and mixed-integer programming problems. Please refer to the `Pyomo documentation `_ for more information. - --------------------------- -CVXPY Support --------------------------- - -CVXPY can be used with near zero code changes: simply select cuOpt as the solver to solve linear and quadratic programs, as well as QCQP/SOCP problems (beta). Please refer to the `CVXPY documentation `_ for more information. diff --git a/docs/cuopt/source/transition.rst b/docs/cuopt/source/transition.rst deleted file mode 100644 index 67395baf56..0000000000 --- a/docs/cuopt/source/transition.rst +++ /dev/null @@ -1,78 +0,0 @@ -======================================= -Transition Guide for Change in Features -======================================= - -In addition to the quality improvements, some new features were added, and some features were deprecated to improve user experience. For any questions, please reach out to the cuOpt team through github issues. - -Parameter/option statuses are listed below, they express how each of these options are affected: - - **New** - A new feature has been added. - - **Update** - A change in definition of feature. - - **Deprecated** - These options will be accepted but will be removed in the future. In the case of the cuOpt service, the server will also return a warning noting that a feature is deprecated. - - **Limited** - These options are limited with respect to the number of dimensions that can be provided. - - **Removed** - These features were deprecated in a previous release and completely removed in this one. - -For all solver_configs fields, see the :doc:`convex-settings` and :doc:`mip-settings` guides or the service openapi spec :doc:`open-api`. - -Changes to solver_configs.tolerances ------------------------------------- - -The following fields are **Deprecated** in ``solver_configs.tolerances`` for the service: - -- absolute_primal -- absolute_dual -- absolute_gap -- relative_primal -- relative_dual -- relative_gap -- primal_infeasible -- dual_infeasible -- integrality_tolerance -- absolute_mip_gap -- relative_mip_gap - -The following fields are **New** in ``solver_configs.tolerances`` for the service and replace the deprecated fields above: - -- absolute_primal_tolerance -- absolute_dual_tolerance -- absolute_gap_tolerance -- relative_primal_tolerance -- relative_dual_tolerance -- relative_gap_tolerance -- primal_infeasible_tolerance -- dual_infeasible_tolerance -- mip_integrality_tolerance -- mip_absolute_gap -- mip_relative_gap - -The following fields are **New** in ``solver_configs.tolerances`` for the service but were available in the C API in 25.05 - -- mip_absolute_tolerance -- mip_relative_tolerance - -Changes to solver_configs -------------------------- - -The following fields are **Deprecated** in ``solver_configs`` for the service: - -- solver_mode -- heuristics_only - -The following fields are **New** in ``solver_configs`` for the service and replace the deprecated fields above: - -- pdlp_solver_mode -- mip_heuristics_only - -The following are **New** in ``solver_configs`` for the service but were available in the C API in 25.05 - -- strict_infeasibility -- user_problem_file -- per_constraint_residual -- save_best_primal_so_far -- first_primal_feasible -- log_file -- solution_file diff --git a/docs/cuopt/source/versions1.json b/docs/cuopt/source/versions1.json deleted file mode 100644 index 095677726d..0000000000 --- a/docs/cuopt/source/versions1.json +++ /dev/null @@ -1,44 +0,0 @@ -[ - { - "version": "26.08.00", - "url": "https://docs.nvidia.com/cuopt/user-guide/26.08.00/", - "name": "latest", - "preferred": true - }, - { - "version": "26.06.00", - "url": "https://docs.nvidia.com/cuopt/user-guide/26.06.00/" - }, - { - "version": "26.04.00", - "url": "https://archive.docs.nvidia.com/cuopt/user-guide/26.04.00/" - }, - { - "version": "26.02.00", - "url": "https://archive.docs.nvidia.com/cuopt/user-guide/26.02.00/" - }, - { - "version": "25.12.00", - "url": "https://archive.docs.nvidia.com/cuopt/user-guide/25.12.00/" - }, - { - "version": "25.10.00", - "url": "https://archive.docs.nvidia.com/cuopt/user-guide/25.10.00/" - }, - { - "version": "25.08.00", - "url": "https://archive.docs.nvidia.com/cuopt/user-guide/25.08.00/" - }, - { - "version": "25.05", - "url": "https://archive.docs.nvidia.com/cuopt/user-guide/25.05.00/" - }, - { - "version": "25.02", - "url": "https://archive.docs.nvidia.com/cuopt/user-guide/25.02/" - }, - { - "version": "24.11", - "url": "https://archive.docs.nvidia.com/cuopt/user-guide/24.11/" - } -] diff --git a/fern/Doxyfile b/fern/Doxyfile new file mode 100644 index 0000000000..3a1005cc29 --- /dev/null +++ b/fern/Doxyfile @@ -0,0 +1,41 @@ +# Doxyfile for fern/extract_c_api.py +# Processes the two cuOpt C API headers and outputs XML only. +# Run from the repo root: doxygen fern/Doxyfile +# XML is written to fern/.doxygen-xml/ (git-ignored). + +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "cuOpt C API" +PROJECT_BRIEF = "NVIDIA cuOpt C API reference" +OUTPUT_DIRECTORY = fern/.doxygen-xml +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +FULL_PATH_NAMES = NO +MARKDOWN_SUPPORT = YES +EXTENSION_MAPPING = h=C +OPTIMIZE_OUTPUT_FOR_C = YES + +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO + +INPUT = cpp/include/cuopt/mathematical_optimization/cuopt_c.h \ + cpp/include/cuopt/mathematical_optimization/constants.h + +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h +RECURSIVE = NO + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SKIP_FUNCTION_MACROS = YES + +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_OUTPUT = xml +XML_PROGRAMLISTING = NO + +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO diff --git a/fern/README.md b/fern/README.md new file mode 100644 index 0000000000..d3b7aa4450 --- /dev/null +++ b/fern/README.md @@ -0,0 +1,71 @@ +# cuOpt Fern Docs + +This directory contains the [Fern](https://buildwithfern.com)-based documentation for NVIDIA cuOpt. + +## Structure + +``` +fern/ +├── docs.yml # Top-level Fern config (tabs, versions, nav) +├── docs-v26-08.yml # Per-version nav and content config +├── docs/pages/ # All MDX content pages +│ ├── cuopt-python/ # Python API docs (generated + examples) +│ ├── cuopt-c/ # C API docs (generated + examples) +│ └── ... # Other feature/guide pages +├── generate_api_docs.py # Master build script (calls all extractors) +├── extract_python_api.py # Python API → MDX (AST + Cython regex) +├── extract_c_api.py # C API → MDX (Doxygen XML) +├── embed_examples.py # Embeds example .py/.c files as code blocks +├── build_docs.sh # Local preview helper +└── openapi/ # OpenAPI spec (auto-generated) +``` + +## Local preview + +Activate your cuOpt conda environment, then run: + +```bash +./build.sh docs +``` + +Opens at http://localhost:3000. Regenerates all API docs before starting. + +## How generated content works + +Three scripts run on every build (`generate_api_docs.py` calls all three): + +| Script | Input | Output | +|---|---|---| +| `extract_python_api.py` | Python source + docstrings | `docs/pages/cuopt-python/*/api.mdx` | +| `extract_c_api.py` | Doxygen XML from C headers | `docs/pages/cuopt-c/*/api.mdx` | +| `embed_examples.py` | `examples/*.py` files next to each MDX page | code blocks in `*-examples.mdx` | + +Never hand-edit generated MDX files — changes are overwritten on the next build. + +## Adding a Python API symbol + +1. Add a docstring (NumPy style) to the class or function in `python/cuopt/`. +2. Make sure the symbol is in the module's `__all__`. +3. Run `python fern/generate_api_docs.py` to regenerate. + +To control which symbols appear or in what order, edit `PAGE_SOURCES` at the top of `extract_python_api.py`. + +## Adding a C API symbol + +1. Add a Doxygen comment to the header in `cpp/include/cuopt/mathematical_optimization/`. +2. Place a `{/* symbol: SYMBOL_NAME */}{/* /symbol */}` marker in the right section of the skeleton MDX file (`docs/pages/cuopt-c/convex/convex-c-api.mdx` or `mip-c-api.mdx`). +3. Run `python fern/generate_api_docs.py` — the marker fills in automatically. + +## Adding an example + +1. Drop a `.py` or `.c` file into the `examples/` subdirectory next to the relevant `*-examples.mdx` page. +2. Add a bare link in the MDX file: `[filename.py](examples/filename.py)` +3. Run `python fern/embed_examples.py` — the link is replaced with a fenced code block. + +## Editing guide pages + +All non-generated pages under `docs/pages/` are plain MDX and can be edited directly. Images go in `docs/images/`. + +## Navigation + +Navigation is defined in `docs-v26-08.yml` (current version). The Python API page entries between the `BEGIN / END auto-generated` markers are managed automatically by `extract_python_api.py`. diff --git a/fern/build_docs.sh b/fern/build_docs.sh new file mode 100755 index 0000000000..26a50f301e --- /dev/null +++ b/fern/build_docs.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Build Fern docs: generate MDX, validate, then preview or publish. +# Usage: +# fern/build_docs.sh # local preview (http://localhost:3000) +# fern/build_docs.sh --publish-docs # publish to Fern cloud (production) +# fern/build_docs.sh --preview # CI PR preview (fern generate --docs --preview) +# +# Prerequisites: node, npm, jq, and a conda environment with Python + numpydoc. +# Run from the repo root. + +set -e + +REPODIR=$(cd "$(dirname "$0")/.."; pwd) +PUBLISH=0 +PREVIEW=0 +for arg in "$@"; do + [[ "$arg" == "--publish-docs" ]] && PUBLISH=1 + [[ "$arg" == "--preview" ]] && PREVIEW=1 +done + +if ! command -v node &>/dev/null || ! command -v npm &>/dev/null; then + echo "ERROR: Node.js (with npm) is required for the Fern CLI." + echo " Install from: https://nodejs.org/ (or: conda install nodejs)" + exit 1 +fi +if ! command -v jq &>/dev/null; then + echo "ERROR: jq is required to read the Fern version pin." + echo " Install: sudo apt-get install jq" + exit 1 +fi + +# Install Fern CLI at the version pinned in fern/fern.config.json +FERN_VERSION=$(jq -r .version "${REPODIR}/fern/fern.config.json") +if ! fern --version 2>/dev/null | grep -q "${FERN_VERSION}"; then + echo "Installing fern-api@${FERN_VERSION}..." + npm install -g "fern-api@${FERN_VERSION}" +fi + +# Regenerate dynamic API reference pages +PY=${PYTHON:-python3} +if command -v "${PY}" &>/dev/null; then + "${PY}" "${REPODIR}/fern/generate_api_docs.py" +else + echo " [WARN] Python not found; skipping API doc generation." +fi + +echo "Running fern check..." +fern check + +if [[ "${PUBLISH}" -eq 1 ]]; then + if [[ -z "${FERN_TOKEN:-}" ]]; then + echo "ERROR: FERN_TOKEN environment variable is not set." + exit 1 + fi + echo "Publishing to Fern cloud..." + fern generate --docs + echo "Docs published to https://nvidia-cuopt.docs.buildwithfern.com" +elif [[ "${PREVIEW}" -eq 1 ]]; then + if [[ -z "${FERN_TOKEN:-}" ]]; then + echo "FERN_TOKEN not set; skipping PR preview publish (fern check already passed above)." + else + echo "Publishing Fern PR preview..." + fern generate --docs --preview + fi +else + echo "" + echo "Starting local preview at http://localhost:3000 (Ctrl+C to stop)..." + fern docs dev +fi diff --git a/fern/docs-v25-05.yml b/fern/docs-v25-05.yml new file mode 100644 index 0000000000..dd6aefdcbc --- /dev/null +++ b/fern/docs-v25-05.yml @@ -0,0 +1,3 @@ +navigation: + - page: cuOpt 25.05 Documentation + path: docs/pages/versions/v25-05.mdx diff --git a/fern/docs-v25-08.yml b/fern/docs-v25-08.yml new file mode 100644 index 0000000000..56c33ba768 --- /dev/null +++ b/fern/docs-v25-08.yml @@ -0,0 +1,3 @@ +navigation: + - page: cuOpt 25.08 Documentation + path: docs/pages/versions/v25-08.mdx diff --git a/fern/docs-v25-12.yml b/fern/docs-v25-12.yml new file mode 100644 index 0000000000..6f6e695fa5 --- /dev/null +++ b/fern/docs-v25-12.yml @@ -0,0 +1,3 @@ +navigation: + - page: cuOpt 25.12 Documentation + path: docs/pages/versions/v25-12.mdx diff --git a/fern/docs-v26-02.yml b/fern/docs-v26-02.yml new file mode 100644 index 0000000000..2a699bb126 --- /dev/null +++ b/fern/docs-v26-02.yml @@ -0,0 +1,3 @@ +navigation: + - page: cuOpt 26.02 Documentation + path: docs/pages/versions/v26-02.mdx diff --git a/fern/docs-v26-04.yml b/fern/docs-v26-04.yml new file mode 100644 index 0000000000..17c5a571ee --- /dev/null +++ b/fern/docs-v26-04.yml @@ -0,0 +1,3 @@ +navigation: + - page: cuOpt 26.04 Documentation + path: docs/pages/versions/v26-04.mdx diff --git a/fern/docs-v26-06.yml b/fern/docs-v26-06.yml new file mode 100644 index 0000000000..01415cc647 --- /dev/null +++ b/fern/docs-v26-06.yml @@ -0,0 +1,3 @@ +navigation: + - page: cuOpt 26.06 Documentation + path: docs/pages/versions/v26-06.mdx diff --git a/fern/docs-v26-08.yml b/fern/docs-v26-08.yml new file mode 100644 index 0000000000..f6f2bcea00 --- /dev/null +++ b/fern/docs-v26-08.yml @@ -0,0 +1,200 @@ +navigation: + - section: NVIDIA cuOpt + contents: + - page: Introduction + path: docs/pages/introduction.mdx + - page: Installation + path: docs/pages/install.mdx + - page: Release Notes + path: docs/pages/release-notes.mdx + - page: System Requirements + path: docs/pages/system-requirements.mdx + - section: Features + contents: + - page: Routing Features + path: docs/pages/routing-features.mdx + - page: Convex Optimization Features + path: docs/pages/convex-features.mdx + - page: MILP Features + path: docs/pages/milp-features.mdx + - section: C API + contents: + - page: Overview + path: docs/pages/cuopt-c/index.mdx + - section: C API Overview + contents: + - page: Quickstart Guide + path: docs/pages/cuopt-c/quick-start.mdx + - section: Convex Optimization (LP/QP/QCQP/SOCP) + contents: + - page: Overview + path: docs/pages/cuopt-c/convex/index.mdx + - section: LP/QP/QCQP/SOCP C API + contents: + - page: cuOpt Convex Optimization C API Reference + path: docs/pages/cuopt-c/convex/convex-c-api.mdx + - page: Convex Optimization Settings + path: docs/pages/convex-settings.mdx + - page: Convex Optimization C API Examples + path: docs/pages/cuopt-c/convex/convex-examples.mdx + - section: Mixed Integer Programming (MIP) + contents: + - page: Overview + path: docs/pages/cuopt-c/mip/index.mdx + - section: MIP C API + contents: + - page: cuOpt MIP C API Reference + path: docs/pages/cuopt-c/mip/mip-c-api.mdx + - page: MIP Settings + path: docs/pages/mip-settings.mdx + - page: MILP C API Examples + path: docs/pages/cuopt-c/mip/mip-examples.mdx + - section: Python API + contents: + - page: Overview + path: docs/pages/cuopt-python/index.mdx + - section: Python API Overview + contents: + - page: Quickstart Guide + path: docs/pages/cuopt-python/quick-start.mdx + - section: Routing Optimization + contents: + - page: Overview + path: docs/pages/cuopt-python/routing/index.mdx + - section: Routing + contents: + - page: cuOpt Routing Python API Reference + path: docs/pages/cuopt-python/routing/routing-api.mdx + - page: Routing Examples + path: docs/pages/cuopt-python/routing/routing-examples.mdx + - section: Convex Optimization (LP/QP/QCQP/SOCP) + contents: + - page: Overview + path: docs/pages/cuopt-python/convex/index.mdx + - section: LP/QP/QCQP/SOCP Python API + contents: + - page: Convex Optimization API Reference + path: docs/pages/cuopt-python/convex/convex-api.mdx + - page: Convex Optimization Settings + path: docs/pages/convex-settings.mdx + - page: Convex Optimization Examples + path: docs/pages/cuopt-python/convex/convex-examples.mdx + - section: Mixed Integer Programming (MIP) + contents: + - page: Overview + path: docs/pages/cuopt-python/mip/index.mdx + - section: MIP Python API + contents: + - page: MIP API Reference + path: docs/pages/cuopt-python/mip/mip-api.mdx + - page: MIP Settings + path: docs/pages/mip-settings.mdx + - page: MIP Examples + path: docs/pages/cuopt-python/mip/mip-examples.mdx + - section: gRPC Remote Execution + contents: + - page: Overview + path: docs/pages/cuopt-grpc/index.mdx + - section: In this section + contents: + - page: Quick Start + path: docs/pages/cuopt-grpc/quick-start.mdx + - page: Advanced Configuration + path: docs/pages/cuopt-grpc/advanced.mdx + - page: Examples + path: docs/pages/cuopt-grpc/examples.mdx + - page: gRPC API (Reference) + path: docs/pages/cuopt-grpc/api.mdx + - page: Grpc Server Architecture + path: docs/pages/cuopt-grpc/grpc-server-architecture.mdx + - section: Server + contents: + - page: Overview + path: docs/pages/cuopt-server/index.mdx + - section: Quickstart + contents: + - page: Quickstart Guide + path: docs/pages/cuopt-server/quick-start.mdx + - section: Server API + contents: + - page: Overview + path: docs/pages/cuopt-server/server-api/index.mdx + - section: Server API Overview + contents: + - page: cuOpt Server CLI + path: docs/pages/cuopt-server/server-api/server-cli.mdx + - api: REST API Reference + - page: Convex Optimization Settings + path: docs/pages/convex-settings.mdx + - page: MIP Settings + path: docs/pages/mip-settings.mdx + - section: Client API + contents: + - page: Overview + path: docs/pages/cuopt-server/client-api/index.mdx + - section: Client API Overview + contents: + - page: "Self-Hosted Service Client API Reference" + path: docs/pages/cuopt-server/client-api/sh-cli-api.mdx + - page: "Build Your Own Self-Hosted Thin Client" + path: docs/pages/cuopt-server/client-api/sh-cli-build.mdx + - section: Examples + contents: + - page: Overview + path: docs/pages/cuopt-server/examples/index.mdx + - section: Examples Overview + contents: + - page: LP Python Examples + path: docs/pages/cuopt-server/examples/lp-examples.mdx + - page: MILP Python Examples + path: docs/pages/cuopt-server/examples/milp-examples.mdx + - page: Routing Python Examples + path: docs/pages/cuopt-server/examples/routing-examples.mdx + - section: CSP Guides + contents: + - page: Overview + path: docs/pages/cuopt-server/csp-guides/index.mdx + - section: CSP Guides Overview + contents: + - page: Azure Quickstart Guide + path: docs/pages/cuopt-server/csp-guides/csp-azure.mdx + - page: AWS Quickstart Guide + path: docs/pages/cuopt-server/csp-guides/csp-aws.mdx + - section: NIM Operator + contents: + - page: Overview + path: docs/pages/cuopt-server/nim-operator/index.mdx + - page: Prerequisites + path: docs/pages/cuopt-server/nim-operator/prerequisites.mdx + - page: Deployment Guide + path: docs/pages/cuopt-server/nim-operator/deployment.mdx + - page: Configuration Guide + path: docs/pages/cuopt-server/nim-operator/configuration.mdx + - section: Command Line Interface + contents: + - page: Overview + path: docs/pages/cuopt-cli/index.mdx + - section: Command Line Interface Overview + contents: + - page: Quickstart Guide + path: docs/pages/cuopt-cli/quick-start.mdx + - section: Usage + contents: + - page: Examples + path: docs/pages/cuopt-cli/cli-examples.mdx + - section: "Third-Party Modeling Languages" + contents: + - page: Overview + path: docs/pages/thirdparty_modeling_languages/index.mdx + - section: Resources + contents: + - page: FAQ + path: docs/pages/faq.mdx + - page: Transition Guide for Change in Features + path: docs/pages/transition.mdx + - page: External Resources + path: docs/pages/resources.mdx + - section: Licenses + contents: + - page: cuOpt License + path: docs/pages/license.mdx diff --git a/fern/docs.yml b/fern/docs.yml new file mode 100644 index 0000000000..1ff2f8e2ea --- /dev/null +++ b/fern/docs.yml @@ -0,0 +1,49 @@ +instances: + - url: nvidia-cuopt.docs.buildwithfern.com + +title: NVIDIA cuOpt + +global-theme: nvidia + +navbar-links: + - type: github + value: https://github.com/NVIDIA/cuopt + +logo: + dark: docs/images/nvidia-cuopt-logo-dark.svg + light: docs/images/nvidia-cuopt-logo-light.svg + href: https://docs.nvidia.com/cuopt/ + +colors: + accentPrimary: + dark: "#76B900" + light: "#76B900" + +css: + - docs/scripts/install-selector.css + +js: + - path: docs/scripts/cuopt-install-version.js + - path: docs/scripts/install-selector.js + +tabs: + docs: + display-name: Documentation + icon: book + +versions: + # rapids-pre-commit-hooks: disable-next-line + - display-name: "26.08 (Latest)" + path: docs-v26-08.yml + - display-name: "26.06" + path: docs-v26-06.yml + - display-name: "26.04" + path: docs-v26-04.yml + - display-name: "26.02" + path: docs-v26-02.yml + - display-name: "25.12" + path: docs-v25-12.yml + - display-name: "25.08" + path: docs-v25-08.yml + - display-name: "25.05" + path: docs-v25-05.yml diff --git a/docs/cuopt/source/images/add_role.png b/fern/docs/images/add_role.png similarity index 100% rename from docs/cuopt/source/images/add_role.png rename to fern/docs/images/add_role.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/aws1.png b/fern/docs/images/aws1.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/aws1.png rename to fern/docs/images/aws1.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/aws2.png b/fern/docs/images/aws2.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/aws2.png rename to fern/docs/images/aws2.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/aws3.png b/fern/docs/images/aws3.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/aws3.png rename to fern/docs/images/aws3.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/aws4.png b/fern/docs/images/aws4.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/aws4.png rename to fern/docs/images/aws4.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/azure1.png b/fern/docs/images/azure1.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/azure1.png rename to fern/docs/images/azure1.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/azure2.png b/fern/docs/images/azure2.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/azure2.png rename to fern/docs/images/azure2.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/azure3.png b/fern/docs/images/azure3.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/azure3.png rename to fern/docs/images/azure3.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/azure4.png b/fern/docs/images/azure4.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/azure4.png rename to fern/docs/images/azure4.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/azure5.png b/fern/docs/images/azure5.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/azure5.png rename to fern/docs/images/azure5.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/azure6.png b/fern/docs/images/azure6.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/azure6.png rename to fern/docs/images/azure6.png diff --git a/docs/cuopt/source/images/cloud_functions.png b/fern/docs/images/cloud_functions.png similarity index 100% rename from docs/cuopt/source/images/cloud_functions.png rename to fern/docs/images/cloud_functions.png diff --git a/docs/cuopt/source/cuopt-python/routing/images/cost_matrix.png b/fern/docs/images/cost_matrix.png similarity index 100% rename from docs/cuopt/source/cuopt-python/routing/images/cost_matrix.png rename to fern/docs/images/cost_matrix.png diff --git a/docs/cuopt/source/images/cuOpt-managed.png b/fern/docs/images/cuOpt-managed.png similarity index 100% rename from docs/cuopt/source/images/cuOpt-managed.png rename to fern/docs/images/cuOpt-managed.png diff --git a/docs/cuopt/source/cuopt-server/csp-guides/images/cuOpt-self-hosted.png b/fern/docs/images/cuOpt-self-hosted.png similarity index 100% rename from docs/cuopt/source/cuopt-server/csp-guides/images/cuOpt-self-hosted.png rename to fern/docs/images/cuOpt-self-hosted.png diff --git a/docs/cuopt/source/images/cuOpt_Collections_Image.png b/fern/docs/images/cuOpt_Collections_Image.png similarity index 100% rename from docs/cuopt/source/images/cuOpt_Collections_Image.png rename to fern/docs/images/cuOpt_Collections_Image.png diff --git a/docs/cuopt/source/images/cuOpt_Stack.png b/fern/docs/images/cuOpt_Stack.png similarity index 100% rename from docs/cuopt/source/images/cuOpt_Stack.png rename to fern/docs/images/cuOpt_Stack.png diff --git a/docs/cuopt/source/images/cuopt_feature_diag.jpg b/fern/docs/images/cuopt_feature_diag.jpg similarity index 100% rename from docs/cuopt/source/images/cuopt_feature_diag.jpg rename to fern/docs/images/cuopt_feature_diag.jpg diff --git a/docs/cuopt/source/images/cuopt_landing_page.png b/fern/docs/images/cuopt_landing_page.png similarity index 100% rename from docs/cuopt/source/images/cuopt_landing_page.png rename to fern/docs/images/cuopt_landing_page.png diff --git a/docs/cuopt/source/images/faq-01.png b/fern/docs/images/faq-01.png similarity index 100% rename from docs/cuopt/source/images/faq-01.png rename to fern/docs/images/faq-01.png diff --git a/docs/cuopt/source/images/generate-personal-key.png b/fern/docs/images/generate-personal-key.png similarity index 100% rename from docs/cuopt/source/images/generate-personal-key.png rename to fern/docs/images/generate-personal-key.png diff --git a/docs/cuopt/source/images/genms_cuopt.png b/fern/docs/images/genms_cuopt.png similarity index 100% rename from docs/cuopt/source/images/genms_cuopt.png rename to fern/docs/images/genms_cuopt.png diff --git a/docs/cuopt/source/cuopt-grpc/images/grpc-job-states.png b/fern/docs/images/grpc-job-states.png similarity index 100% rename from docs/cuopt/source/cuopt-grpc/images/grpc-job-states.png rename to fern/docs/images/grpc-job-states.png diff --git a/docs/cuopt/source/cuopt-grpc/images/grpc-process-model.png b/fern/docs/images/grpc-process-model.png similarity index 100% rename from docs/cuopt/source/cuopt-grpc/images/grpc-process-model.png rename to fern/docs/images/grpc-process-model.png diff --git a/docs/cuopt/source/images/image1.png b/fern/docs/images/image1.png similarity index 100% rename from docs/cuopt/source/images/image1.png rename to fern/docs/images/image1.png diff --git a/docs/cuopt/source/images/image2.png b/fern/docs/images/image2.png similarity index 100% rename from docs/cuopt/source/images/image2.png rename to fern/docs/images/image2.png diff --git a/docs/cuopt/source/images/image3.png b/fern/docs/images/image3.png similarity index 100% rename from docs/cuopt/source/images/image3.png rename to fern/docs/images/image3.png diff --git a/docs/cuopt/source/images/image4.png b/fern/docs/images/image4.png similarity index 100% rename from docs/cuopt/source/images/image4.png rename to fern/docs/images/image4.png diff --git a/docs/cuopt/source/images/image5.png b/fern/docs/images/image5.png similarity index 100% rename from docs/cuopt/source/images/image5.png rename to fern/docs/images/image5.png diff --git a/docs/cuopt/source/images/invite_user.png b/fern/docs/images/invite_user.png similarity index 100% rename from docs/cuopt/source/images/invite_user.png rename to fern/docs/images/invite_user.png diff --git a/docs/cuopt/source/images/ms_cuopt.png b/fern/docs/images/ms_cuopt.png similarity index 100% rename from docs/cuopt/source/images/ms_cuopt.png rename to fern/docs/images/ms_cuopt.png diff --git a/docs/cuopt/source/images/nav-cloud-functions.png b/fern/docs/images/nav-cloud-functions.png similarity index 100% rename from docs/cuopt/source/images/nav-cloud-functions.png rename to fern/docs/images/nav-cloud-functions.png diff --git a/docs/cuopt/source/images/nav-landing-page.png b/fern/docs/images/nav-landing-page.png similarity index 100% rename from docs/cuopt/source/images/nav-landing-page.png rename to fern/docs/images/nav-landing-page.png diff --git a/fern/docs/images/nvidia-cuopt-logo-dark.svg b/fern/docs/images/nvidia-cuopt-logo-dark.svg new file mode 100644 index 0000000000..a05eb02ff2 --- /dev/null +++ b/fern/docs/images/nvidia-cuopt-logo-dark.svg @@ -0,0 +1,4 @@ + + NVIDIA + cuOpt + diff --git a/fern/docs/images/nvidia-cuopt-logo-light.svg b/fern/docs/images/nvidia-cuopt-logo-light.svg new file mode 100644 index 0000000000..926d502147 --- /dev/null +++ b/fern/docs/images/nvidia-cuopt-logo-light.svg @@ -0,0 +1,4 @@ + + NVIDIA + cuOpt + diff --git a/docs/cuopt/source/images/options_cuopt.png b/fern/docs/images/options_cuopt.png similarity index 100% rename from docs/cuopt/source/images/options_cuopt.png rename to fern/docs/images/options_cuopt.png diff --git a/docs/cuopt/source/images/organization.png b/fern/docs/images/organization.png similarity index 100% rename from docs/cuopt/source/images/organization.png rename to fern/docs/images/organization.png diff --git a/docs/cuopt/source/images/overview_cuopt.png b/fern/docs/images/overview_cuopt.png similarity index 100% rename from docs/cuopt/source/images/overview_cuopt.png rename to fern/docs/images/overview_cuopt.png diff --git a/docs/cuopt/source/images/route_map.png b/fern/docs/images/route_map.png similarity index 100% rename from docs/cuopt/source/images/route_map.png rename to fern/docs/images/route_map.png diff --git a/docs/cuopt/source/images/service_key_creation.png b/fern/docs/images/service_key_creation.png similarity index 100% rename from docs/cuopt/source/images/service_key_creation.png rename to fern/docs/images/service_key_creation.png diff --git a/docs/cuopt/source/images/service_keys.png b/fern/docs/images/service_keys.png similarity index 100% rename from docs/cuopt/source/images/service_keys.png rename to fern/docs/images/service_keys.png diff --git a/docs/cuopt/source/images/sh-cuOpt-stack.png b/fern/docs/images/sh-cuOpt-stack.png similarity index 100% rename from docs/cuopt/source/images/sh-cuOpt-stack.png rename to fern/docs/images/sh-cuOpt-stack.png diff --git a/docs/cuopt/source/images/shared_functions.png b/fern/docs/images/shared_functions.png similarity index 100% rename from docs/cuopt/source/images/shared_functions.png rename to fern/docs/images/shared_functions.png diff --git a/docs/cuopt/source/images/soft_stack.png b/fern/docs/images/soft_stack.png similarity index 100% rename from docs/cuopt/source/images/soft_stack.png rename to fern/docs/images/soft_stack.png diff --git a/docs/cuopt/source/images/users.png b/fern/docs/images/users.png similarity index 100% rename from docs/cuopt/source/images/users.png rename to fern/docs/images/users.png diff --git a/docs/cuopt/source/cuopt-python/routing/images/waypoint_graph.png b/fern/docs/images/waypoint_graph.png similarity index 100% rename from docs/cuopt/source/cuopt-python/routing/images/waypoint_graph.png rename to fern/docs/images/waypoint_graph.png diff --git a/fern/docs/pages/convex-features.mdx b/fern/docs/pages/convex-features.mdx new file mode 100644 index 0000000000..4bb42ecab4 --- /dev/null +++ b/fern/docs/pages/convex-features.mdx @@ -0,0 +1,221 @@ +--- +title: "Convex Optimization Features" +--- + +## Availability + +The convex optimization solvers for Linear Programming (LP), Quadratic Programming (QP), Quadratically Constrained Quadratic Programming (QCQP), and Second-Order Cone Programming (SOCP) can be accessed in the following ways: + + - **Third-Party Modeling Languages**: cuOpt's convex optimization solvers can be called directly from the following third-party modeling languages. This allows you to GPU-accelerate your existing optimization workflow in these modeling languages. + + Supported modeling languages: + + | Language | LP | QP | QCQP/SOCP | + | -------- | -- | -- | --------- | + | AMPL | ✓ | | | + | CVXPY | ✓ | ✓ | ✓ | + | GAMS | ✓ | ✓ | | + | JuMP | ✓ | ✓ | | + | PuLP | ✓ | | | + | Pyomo | ✓ | | | + + + +QCQP/SOCP support is currently in **beta**, and is only supported in CVXPY among modeling languages. We hope to add support for QCQP/SOCP in other modeling languages soon. + + + - **C API**: A native C API that provides direct low-level access to cuOpt's convex optimization solvers, enabling integration into any application or system that can interface with C. + - **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 [Quick Start](cuopt-python/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. + +## Variable Bounds + +Lower and upper bounds can be applied to each variable. If no variable bounds are specified, the default bounds are `[-inf,+inf]`. + +## Constraints + +The constraint matrix is specified in [Compressed Sparse Row (CSR) format](https://docs.nvidia.com/cuda/cusparse/#compressed-sparse-row-csr). + +There are two ways to specify constraints to the LP solver: + +1. Using row\_type and right-hand side: + + Constraints can be specified in the form: + + A\*x \{<=, =, \>=\} b + + where A is the constraint matrix in CSR format, x is the variable vector, and b is the right-hand side vector. The relationship \{<=, =, \>=\} is specified via the `row_type` parameter. + +2. Using constraint bounds: + + Alternatively, constraints can be specified as two-sided inequalities: + + lb <= A\*x <= ub + + where lb and ub are vectors of lower and upper bounds respectively. This form allows specifying both bounds on a single constraint. + +## Quadratic Objectives + +cuOpt supports problems with quadratic objectives of the form: + +where H = (Q + Q^T)/2 is a symmetric positive semidefinite matrix. Note that the Q matrix need not be symmetric, and is specified without the 1/2 factor that may be used by other solvers. + + +Currently, barrier is the only method that supports quadratic objectives. + + +See [simple-qp-example-python](#simple-qp-example-python) for an example of how to create a problem with a quadratic objective using the Python Modeling API. See [simple-qp-example-c](#simple-qp-example-c) for an example of how to create a problem with a quadratic objective using the C API. + +## Quadratic Constraints (Beta) + + +Support for quadratic constraints is currently in **beta**. + + +cuOpt supports quadratic constraints of the form + +and translates them internally into **second-order cone** constraints. Problems with quadratic constraints have the form: + +Quadratic constraints are supplied via `addConstraint` in Python, via :c`cuOptAddQuadraticConstraint` in C, and via `QCMATRIX` sections in MPS. + +cuOpt accepts several different types of quadratic constraints: + +**Convex quadratic constraints**: + +These are constraints of the form + +where `H = (Q + Q^T)/2` is a symmetric positive semidefinite matrix, and + +where `H = (Q + Q^T)/2` is a symmetric negative semidefinite matrix. Note when specifying a convex quadratic constraint, the Q matrix need not be symmetric. + +**Second-order cone constraints**: + +These constrains are of the form: + +Second-order cone constraints must be specified as a quadratic constraint with a bound on the x\_0 variable: + +**Rotated second-order cone constraints**: + +These constraints are of the form: + +> x\_2^2 + x\_3^2 + ... + x\_n^2 <= 2 \* x\_0 \* x\_1, x\_0 \>= 0, x\_1 \>= 0. + +Rotated second-order cone constraints must be specified as a quadratic constraint with a bounds on the x\_0 and x\_1 variables: + +In the Python and C APIs, supply one cross coefficient per variable pair (e.g. `-2 * x_0 * x_1` in the quadratic expression). cuOpt canonicalizes input to one stored COO entry per pair. + +For example, a single API term `-2 * x_0 * x_1` is stored as `-2` and defines the cone `||tail||^2 <= 2 * x_0 * x_1`. + +When any quadratic constraint is present, cuOpt automatically selects the barrier method and disables presolve optimizations that apply only to linear problems. + +.note: + + - Only ``<=`` and ``>=`` sense is supported. Equality quadratic constraints are not supported. + +**Python example — second-order cone** `||(x_1, x_2)||_2 <= x_0`: + +**Python example — rotated cone** `x_2^2 + x_3^2 <= 2 * x_0 * x_1`: + +**C API:** Use :c`cuOptAddQuadraticConstraint` to add convex quadratic constraints or second-order and rotated second-order cone constraints expressed as quadratic inequalities. + + +Problems with quadratic constraints always use the barrier solver regardless of the `CUOPT_METHOD` setting. + + +## Warm Start + +A warm starts allow a user to provide an initial solution to help PDLP converge faster. The initial `primal` and `dual` solutions can be specified by the user. + +Alternatively, previously run solutions can be used to warm start a new solve to decrease solve time. [Examples](#warm-start) are shared on the self-hosted page. + +## PDLP Solver Mode + +Users can control how the solver will operate by specifying the PDLP solver mode. The mode choice can drastically impact how fast a specific problem will be solved. Users are encouraged to test different modes to see which one fits the best their problem. + +## Method + +**Concurrent**: The default method for solving linear programs. When concurrent is selected, cuOpt runs three algorithms in parallel: PDLP on the GPU, barrier (interior-point) on the GPU, and dual simplex on the CPU. A solution is returned from the algorithm that finishes first. + +**PDLP**: Primal-Dual Hybrid Gradient for Linear Program is an algorithm for solving large-scale linear programming problems on the GPU. PDLP does not attempt any matrix factorizations during the course of the solve. Select this method if your LP is so large that factorization will not fit into memory. By default PDLP solves to low relative tolerance and the solutions it returns do not lie at a vertex of the feasible region. Enable crossover to obtain a highly accurate basic solution from a PDLP solution. + + +PDLP solves to 1e-4 relative accuracy by default. + + +**Barrier**: The barrier method (also known as interior-point method) solves linear and quadratic programs using a primal-dual predictor-corrector algorithm. This method uses GPU-accelerated sparse Cholesky and sparse LDLT solves via cuDSS, and GPU-accelerated sparse matrix-vector and matrix-matrix operations via cuSparse. Barrier is particularly effective for large-scale problems and can automatically apply techniques like folding, dualization, and dense column elimination to improve performance. This method solves the linear systems at each iteration using the augmented system or the normal equations (ADAT). Enable crossover to obtain a highly accurate basic solution from a barrier solution. + + +Barrier solves to 1e-8 relative accuracy by default. + + +**Dual Simplex**: Dual simplex is the simplex method applied to the dual of the linear program. Dual simplex requires the basis factorization of linear program fit into memory. Select this method if your LP is small to medium sized, or if you require a high-quality basic solution. + + +Dual Simplex solves to 1e-6 absolute accuracy by default. + + +## Crossover + +Crossover allows you to obtain a high-quality basic solution from the results of a PDLP or barrier solve. When enabled, crossover converts the PDLP or barrier solution to a vertex solution (basic solution) with high accuracy. More details can be found [here](#crossover). + + +Crossover is not supported for problems with quadratic objectives or quadratic constraints. + + +## Presolve + +Presolve procedure is applied to the problem before the solver is called. It can be used to reduce the problem size and improve solve time. cuOpt supports presolve reductions using PSLP or Papilo for linear programming (LP) problems. For LP problems, PSLP presolve is always enabled by default. Users can manually select to disable presolve by setting this parameter to 0, enable Papilo presolve by setting this parameter to 1, or enable PSLP presolve by setting this parameter to 2. Furthermore, for LP problems with Papilo presolver, when the dual solution is not needed, additional presolve procedures can be applied to further improve solve times. This is achieved by turning off dual postsolve with the `CUOPT_DUAL_POSTSOLVE` setting. + +## Logging + +The `CUOPT_LOG_FILE` parameter can be set to write detailed solver logs for LP/QP/QCQP/SOCP problems. This parameter is available in all APIs that allow setting solver parameters except the cuOpt service. For the service, see the logging callback below. + +## Logging Callback in the Service + +In the cuOpt service API, the `log_file` value in `solver_configs` is ignored. + +If however you set the `solver_logs` flag on the `/cuopt/request` REST API call, users can fetch the log file content from the webserver at `/cuopt/logs/{id}`. Using the logging callback feature through the cuOpt client is shown in [Examples](#generic-example-with-normal-and-batch-mode) on the self-hosted page. + +## Infeasibility Detection + +The PDLP solver includes the option to detect infeasible problems. If the infeasibilty detection is enabled in solver settings, PDLP will abort as soon as it concludes the problem is infeasible. + + +Infeasibility detection is always enabled for dual simplex. + + +## Time Limit + +The user may specify a time limit to the solver. By default the solver runs until a solution is found or the problem is determined to be infeasible or unbounded. + + +Note that `time_limit` applies only to solve time inside the LP solver. This does not include time for network transfer, validation of input, and other operations that occur outside the solver. The overhead associated with these operations are usually small compared to the solve time. + + +## Batch Mode + +Users can submit a set of problems which will be solved in a batch. Problems will be solved at the same time in parallel to fully utilize the GPU. Checkout [self-hosted client](#generic-example-with-normal-and-batch-mode) example in thin client. + + +Deprecated + +LP batch mode (Python `cuopt.linear_programming.BatchSolve`, server requests with a list of LP problems, and multi-file `cuopt_sh` LP submissions) is deprecated and will be removed in a future release. Prefer sequential `cuopt.linear_programming.Solve` calls, or implement your own parallelism (for example with `concurrent.futures`). Existing batch APIs still run in parallel today; callers may see a `DeprecationWarning` or a deprecation message in server `warnings`. + + +## PDLP Precision Modes + +By default, PDLP operates in the native precision of the problem type (FP64 for double-precision problems). The `pdlp_precision` parameter provides several modes: + + - **single**: Run PDLP internally in FP32, with automatic conversion of inputs and outputs. FP32 uses half the memory and allows PDHG iterations to be on average twice as fast, but may require more iterations to converge. Compatible with crossover (solution is converted back to FP64 before crossover) and concurrent mode (PDLP runs in FP32 while other solvers run in FP64). + - **mixed**: Use mixed precision SpMV during PDHG iterations. The constraint matrix is stored in FP32 while vectors and compute type remain in FP64, improving SpMV performance with limited impact on convergence. Convergence checking and restart logic always use the full FP64 matrix. + - **double**: Explicitly run in FP64 (same as default for double-precision problems). + + +The default precision is the native type of the problem (FP64 for double). + + +## Multi-GPU Mode + +Users can use multiple GPUs to solve a problem by specifying the `num_gpus` parameter. The feature is restricted to LP problems that uses concurrent mode and supports up to 2 GPUs at the moment. Using this mode will run PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources. diff --git a/fern/docs/pages/convex-settings.mdx b/fern/docs/pages/convex-settings.mdx new file mode 100644 index 0000000000..6e8ec7bba8 --- /dev/null +++ b/fern/docs/pages/convex-settings.mdx @@ -0,0 +1,350 @@ +--- +title: "Convex Optimization Settings" +--- + +This page describes the parameter settings available for cuOpt's convex optimization solvers. These parameters are set as [parameter constants](#parameter-constants) in case of C API and in case of Server Thin client as raw strings. Refer to examples in [C](cuopt-c/convex/index) and [Server Thin client](cuopt-server/index) for more details. + + +When setting parameters in thin client solver settings, remove `CUOPT_` from the parameter name and convert to lowercase. For example, `CUOPT_TIME_LIMIT` would be set as `time_limit`. + + +## Common Parameters + +We begin by describing parameters common to both the MILP and LP solvers + +### Time Limit + +`CUOPT_TIME_LIMIT` controls the time limit in seconds after which the solver will stop and return the current solution. For performance reasons, cuOpt does not constantly checks for time limit. Thus, the solver may run slightly over the limit. If set along with the iteration limit, cuOpt will stop when the first limit (iteration or time) is hit. + + +By default there is no time limit. So cuOpt will run until it finds an optimal solution, or proves the problem is infeasible or unbounded. + + +### Log to Console + +`CUOPT_LOG_TO_CONSOLE` controls whether cuOpt should log information to the console during a solve. If true, a logging info is written to the console, if false no logging info is written to the console (logs may still be written to a file.) + + +The default value is true. + + +### Log File + +`CUOPT_LOG_FILE` controls the name of a log file where cuOpt should write information about the solve. + + +The default value is `""` and no log file is written. This setting is ignored by the cuOpt service, use the log callback feature instead. + + +### Solution File + +`CUOPT_SOLUTION_FILE` controls the name of a file where cuOpt should write the solution. + + +The default value is `""` and no solution file is written. This setting is ignored by the cuOpt service. + + +### User Problem File + +`CUOPT_USER_PROBLEM_FILE` controls the name of a file where cuOpt should write the user problem. + + +The default value is `""` and no user problem file is written. This setting is ignored by the cuOpt service. + + +### Num CPU Threads + +`CUOPT_NUM_CPU_THREADS` controls the number of CPU threads used in the LP and MIP solvers. Set this to a small value to limit the amount of CPU resources cuOpt uses. Set this to a large value to improve solve times for CPU parallel parts of the solvers. + + +By default the number of CPU threads is automatically determined based on the number of CPU cores. + + +### Presolve + +`CUOPT_PRESOLVE` controls which presolver to use for presolve reductions. cuOpt provides presolve reductions for linear programming (LP) problems using either PSLP or Papilo, and for mixed-integer programming (MIP) problems using Papilo. By default, Papilo presolve is always enabled for MIP problems. For LP problems, PSLP presolve is always enabled by default. You can explicitly control the presolver by setting this parameter to 0 (disable presolve), 1 (Papilo), or 2 (PSLP). + +### Dual Postsolve + +`CUOPT_DUAL_POSTSOLVE` controls whether dual postsolve is enabled when using Papilo presolver for LP problems. Disabling dual postsolve can improve solve time at the expense of not having access to the dual solution. Enabled by default for LP when Papilo presolve is selected. This is not relevant for MIP problems. + +## Linear Programming + +We now describe the parameter settings used to control cuOpt's Linear Programming solvers + +### Method + +`CUOPT_METHOD` controls the method to solve the linear programming problem. Four methods are available: + + - `Concurrent`: Use PDLP, dual simplex, and barrier in parallel (default). + - `PDLP`: Use the PDLP method. + - `Dual Simplex`: Use the dual simplex method. + - `Barrier`: Use the barrier (interior-point) method. + + +The default method is `Concurrent`. + + +Default accuracy for each method: + + - PDLP solves to 1e-4 relative accuracy by default. + - Barrier solves to 1e-8 relative accuracy by default. + - Dual Simplex solves to 1e-6 *absolute* accuracy by default. + +C API users should use the constants defined in [method-constants](#method-constants) for this parameter. + +Server Thin client users should use the `cuopt_sh_client.SolverMethod` for this parameter. + +### PDLP Solver Mode + +`CUOPT_PDLP_SOLVER_MODE` controls the mode under which PDLP should operate. The mode will change the way the PDLP internally optimizes the problem. The mode choice can drastically impact how fast a specific problem will be solved. Users are encouraged to test different modes to see which one fits the best their problem. By default, the solver uses `Stable3`, the best overall mode from our experiments. For now, only three modes are available: `Stable3`, `Methodical1`, and `Fast1`. + +For now, we do not offer a mechanism to know upfront which solver mode will be the best for a specific problem. + +C API users should use the constants defined in [pdlp-solver-mode-constants](#pdlp-solver-mode-constants) for this parameter. + +Server Thin client users should use the `cuopt_sh_client.PDLPSolverMode` for this parameter. + +### Iteration Limit + +`CUOPT_ITERATION_LIMIT` controls the iteration limit after which the solver will stop and return the current solution. For performance reasons, cuOpt's does not constantly checks for iteration limit, thus, the solver might run a few extra iterations over the limit. If set along with the time limit, cuOpt will stop at the first limit (iteration or time) reached. + + +By default there is no iteration limit. So, cuOpt will run until it finds an optimal solution, or proves the problem is infeasible or unbounded. + + +### Number of GPUs + +`CUOPT_NUM_GPUS` controls the number of GPUs to use for the solve. This setting is only relevant for LP problems that uses concurrent mode and supports up to 2 GPUs at the moment. Using this mode will run PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources. + +### Infeasibility Detection + +`CUOPT_INFEASIBILITY_DETECTION` controls whether PDLP should detect infeasibility. Note that infeasibility detection in PDLP is not always accurate. Some problems detected as infeasible may converge under a different tolerance factor. Detecting infeasibility consumes both more runtime and memory. The added runtime is between 3% and 7%, the added memory consumption is between 10% and 20%. + + +By default PDLP will not detect infeasibility. Dual simplex will always detect infeasibility regardless of this setting. + + +### Strict Infeasibility + +`CUOPT_STRICT_INFEASIBILITY` controls the strict infeasibility mode in PDLP. When true if either the current or the average solution is detected as infeasible, PDLP will stop. When false both the current and average solution need to be detected as infeasible for PDLP to stop. + + +The default value is false. + + +### Crossover + +`CUOPT_CROSSOVER` controls whether PDLP or barrier should crossover to a basic solution after an optimal solution is found. Changing this value has a significant impact on accuracy and runtime. By default the solutions provided by PDLP and barrier do not lie at a vertex and thus may have many variables that lie between their bounds. Enabling crossover allows the user to obtain a high-quality basic solution that lies at a vertex of the feasible region. If n is the number of variables, and m is the number of constraints, n - m variables will be on their bounds in a basic solution. + + +The default value is false. + + +### Save Best Primal So Far + +`CUOPT_SAVE_BEST_PRIMAL_SOLUTION` controls whether PDLP should save the best primal solution so far With this parameter set to true, PDLP \* Will always prioritize a primal feasible to a non primal feasible \* If a new primal feasible is found, the one with the best primal objective will be kept \* If no primal feasible was found, the one with the lowest primal residual will be kept \* If two have the same primal residual, the one with the best objective will be kept + + +The default value is false. + + +### First Primal Feasible + +`CUOPT_FIRST_PRIMAL_FEASIBLE` controls whether PDLP should stop when the first primal feasible solution is found. + + +The default value is false. + + +### Per Constraint Residual + +`CUOPT_PER_CONSTRAINT_RESIDUAL` controls whether PDLP should compute the primal & dual residual per constraint instead of globally. + + +The default value is false. + + +### PDLP Precision + +`CUOPT_PDLP_PRECISION` controls the precision mode used by the PDLP solver. The following modes are available: + + - **default** (-1): Use the native precision of the problem type (FP64 for double-precision problems). + - **single** (0): Run PDLP internally in FP32 (float). Inputs are converted from FP64 to FP32 before solving and outputs are converted back to FP64. FP32 uses half the memory and allows PDHG iterations to be on average twice as fast, but may require more iterations to converge due to reduced numerical accuracy. Compatible with crossover (solution is converted back to FP64 before crossover runs) and concurrent mode (the PDLP leg runs in FP32 while Dual Simplex and Barrier run in FP64). + - **double** (1): Explicitly run in FP64 (same as default for double-precision problems). + - **mixed** (2): Use mixed precision sparse matrix-vector products (SpMV) during PDHG iterations. The constraint matrix and its transpose are stored in FP32 while vectors and the compute type remain in FP64, improving SpMV performance. Convergence checking and restart logic always use the full FP64 matrix, so this mode does not reduce overall memory usage. This provides a middle ground between full FP64 and FP32: faster PDHG iterations with limited impact on convergence. + + +The default value is 0 (default precision). + + +### Barrier Solver Settings + +The following settings control the behavior of the barrier (interior-point) method: + +#### Folding + +`CUOPT_FOLDING` controls whether to fold the linear program. Folding can reduce problem size by exploiting symmetry in the problem. + + - `-1`: Automatic (default) - cuOpt decides whether to fold based on problem characteristics + - `0`: Disable folding + - `1`: Force folding to run + + +The default value is `-1` (automatic). + + +#### Dualize + +`CUOPT_DUALIZE` controls whether to dualize the linear program in presolve. Dualizing can improve solve time for problems, with inequality constraints, where there are more constraints than variables. + + - `-1`: Automatic (default) - cuOpt decides whether to dualize based on problem characteristics + - `0`: Don't attempt to dualize + - `1`: Force dualize + + +The default value is `-1` (automatic). + + +#### Ordering + +`CUOPT_ORDERING` controls the ordering algorithm used by cuDSS for sparse factorizations. The ordering can significantly impact solver run time. + + - `-1`: Automatic (default) - cuOpt selects the best ordering + - `0`: cuDSS default ordering + - `1`: AMD (Approximate Minimum Degree) ordering + + +The default value is `-1` (automatic). + + +#### Augmented System + +`CUOPT_AUGMENTED` controls which linear system to solve in the barrier method. + + - `-1`: Automatic (default) - cuOpt selects the best linear system to solve + - `0`: Solve the ADAT system (normal equations) + - `1`: Solve the augmented system + + +The default value is `-1` (automatic). The augmented system may be more stable for some problems, while ADAT may be faster for others. + + +#### Eliminate Dense Columns + +`CUOPT_ELIMINATE_DENSE_COLUMNS` controls whether to eliminate dense columns from the constraint matrix before solving. Eliminating dense columns can improve performance by reducing fill-in during factorization. However, extra solves must be performed at each iteration. + + - `true`: Eliminate dense columns (default) + - `false`: Don't eliminate dense columns + +This setting only has an effect when the ADAT (normal equation) system is solved. + + +The default value is `true`. + + +#### cuDSS Deterministic Mode + +`CUOPT_CUDSS_DETERMINISTIC` controls whether cuDSS operates in deterministic mode. Deterministic mode ensures reproducible results across runs but may be slower. + + - `true`: Use deterministic mode + - `false`: Use non-deterministic mode (default) + + +The default value is `false`. Enable deterministic mode if reproducibility is more important than performance. + + +#### Dual Initial Point + +`CUOPT_BARRIER_DUAL_INITIAL_POINT` controls the method used to compute the dual initial point for the barrier solver. The choice of initial point will affect the number of iterations performed by barrier. + + - `-1`: Automatic (default) - cuOpt selects the best method + - `0`: Use an initial point from a heuristic approach based on the paper "On Implementing Mehrotra's Predictor–Corrector Interior-Point Method for Linear Programming" (SIAM J. Optimization, 1992) by Lustig, Martsten, Shanno. + - `1`: Use an initial point from solving a least squares problem that minimizes the norms of the dual variables and reduced costs while statisfying the dual equality constraints. + + +The default value is `-1` (automatic). + + +### Absolute Primal Tolerance + +`CUOPT_ABSOLUTE_PRIMAL_TOLERANCE` controls the absolute primal tolerance used in the primal feasibility check. Changing this value might have a significant impact on accuracy and runtime if the relative part (the right-hand side vector b L2 norm) is close to, or equal to, 0. + +The primal feasibility condition is computed as follows: + + primal_feasiblity < absolute_primal_tolerance + relative_primal_tolerance * l2_norm(b) + +Default value is `1e-4`. + +### Relative Primal Tolerance + +`CUOPT_RELATIVE_PRIMAL_TOLERANCE` controls the relative primal tolerance used in PDLP's primal feasibility check. Changing this value has a significant impact on accuracy and runtime. The primal feasibility condition is computed as follows: + + primal_feasiblity < absolute_primal_tolerance + relative_primal_tolerance * l2_norm(b) + + +The default value is `1e-4`. + + +### Absolute Dual Tolerance + +`CUOPT_ABSOLUTE_DUAL_TOLERANCE` controls the absolute dual tolerance used in PDLP's dual feasibility check. Changing this value might have a significant impact on accuracy and runtime if the relative part (the objective vector L2 norm) is close to, or equal to, 0. + +The dual feasibility condition is computed as follows: + + dual_feasiblity < absolute_dual_tolerance + relative_dual_tolerance * l2_norm(c) + + +The default value is `1e-4`. + + +### Relative Dual Tolerance + +`CUOPT_RELATIVE_DUAL_TOLERANCE` controls the relative dual tolerance used in PDLP's dual feasibility check. Changing this value has a significant impact on accuracy and runtime. The dual feasibility condition is computed as follows: + + dual_feasiblity < absolute_dual_tolerance + relative_dual_tolerance * l2_norm(c) + + +The default value is `1e-4`. + + +### Absolute Gap Tolerance + +`CUOPT_ABSOLUTE_GAP_TOLERANCE` controls the absolute gap tolerance used in PDLP's duality gap check. Changing this value might have a significant impact on accuracy and runtime if the relative part `(|primal_objective| + |dual_objective|)` is close to, or equal to, 0. + +The duality gap is computed as follows: + + duality_gap < absolute_gap_tolerance + relative_gap_tolerance * (|primal_objective| + |dual_objective|) + + +The default value is `1e-4`. + + +### Relative Gap Tolerance + +`CUOPT_RELATIVE_GAP_TOLERANCE` controls the relative gap tolerance used in PDLP's duality gap check. Changing this value has a significant impact on accuracy and runtime. The duality gap is computed as follows: + + duality_gap < absolute_gap_tolerance + relative_gap_tolerance * (|primal_objective| + |dual_objective|) + + +The default value is `1e-4`. + + +### Barrier Iterative Refinement + +`CUOPT_BARRIER_ITERATIVE_REFINEMENT` controls whether iterative refinement is applied after each barrier iteration to improve solution accuracy. + + - `0` (`CUOPT_BARRIER_ITERATIVE_REFINEMENT_OFF`): Disable iterative refinement (default). + - `1` (`CUOPT_BARRIER_ITERATIVE_REFINEMENT_ON`): Enable iterative refinement. + + +The default value is `0` (off). + + +### Barrier Step Scale + +`CUOPT_BARRIER_STEP_SCALE` controls the scaling factor applied to the step size in the barrier method. The step scale must be strictly less than 1. A value like 0.9 is conservative, while a value like 0.999 is aggressive. + + +By default cuOpt selects the step scale automatically. + diff --git a/fern/docs/pages/cuopt-c-api-reference.mdx b/fern/docs/pages/cuopt-c-api-reference.mdx new file mode 100644 index 0000000000..9475782fda --- /dev/null +++ b/fern/docs/pages/cuopt-c-api-reference.mdx @@ -0,0 +1,9 @@ +--- +title: "C API Reference" +--- + + +C API reference is generated from Doxygen XML. Full integration is pending Fern C++ library support (coming soon). + +In the meantime, see the [C API Quick Start](cuopt-c/quick-start) and the [Doxygen source](https://github.com/NVIDIA/cuopt/tree/main/cpp/doxygen). + diff --git a/fern/docs/pages/cuopt-c/convex/convex-c-api.mdx b/fern/docs/pages/cuopt-c/convex/convex-c-api.mdx new file mode 100644 index 0000000000..37060de74c --- /dev/null +++ b/fern/docs/pages/cuopt-c/convex/convex-c-api.mdx @@ -0,0 +1,1188 @@ +--- +title: "cuOpt Convex Optimization C API Reference" +--- + +This section contains the cuOpt convex optimization C API reference. +For MIP-specific functions and callbacks, see [MIP C API](mip-c-api). + +## Integer and Floating-Point Types + +cuOpt may be built with 32 or 64 bit integer and floating-point types. +The C API uses a `typedef` for floating point and integer types to abstract the size of these types. + +{/* symbol: cuopt_int_t */} +**`typedef`** **`cuopt_int_t`** — The type of the integer number used by the solver. Use `cuOptGetIntSize` to get the size of the integer type. (`typedef int32_t`) +{/* /symbol */} + +{/* symbol: cuopt_float_t */} +**`typedef`** **`cuopt_float_t`** — The type of the floating point number used by the solver. Use `cuOptGetFloatSize` to get the size of the floating point type. (`typedef double`) +{/* /symbol */} + +You may use the following functions to determine the number of bytes used to represent these types in your build. + +{/* symbol: cuOptGetIntSize */} +#### `cuOptGetIntSize(void) -> int8_t` + +Get the size of the integer type used by the library. + +**Returns** The size of the integer type in bytes. +{/* /symbol */} + +{/* symbol: cuOptGetFloatSize */} +#### `cuOptGetFloatSize(void) -> int8_t` + +Get the size of the float type. + +**Returns** The size in bytes of the float type. +{/* /symbol */} + +## Version Information + +You may use the following function to get the version of the cuOpt library. + +{/* symbol: cuOptGetVersion */} +#### `cuOptGetVersion(cuopt_int_t * version_major, cuopt_int_t * version_minor, cuopt_int_t * version_patch) -> cuopt_int_t` + +Get the version of the library. + +**Parameters** + +- **`version_major`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that will contain the major version number. +- **`version_minor`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that will contain the minor version number. +- **`version_patch`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that will contain the patch version number. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +## Status Codes + +Every function in the C API returns a status code that indicates success or failure. +The following status codes are defined. + +{/* symbol: CUOPT_SUCCESS */} +- `CUOPT_SUCCESS` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_INVALID_ARGUMENT */} +- `CUOPT_INVALID_ARGUMENT` (`1`) +{/* /symbol */} + +{/* symbol: CUOPT_MPS_FILE_ERROR */} +- `CUOPT_MPS_FILE_ERROR` (`2`) +{/* /symbol */} + +{/* symbol: CUOPT_MPS_PARSE_ERROR */} +- `CUOPT_MPS_PARSE_ERROR` (`3`) +{/* /symbol */} + +{/* symbol: CUOPT_VALIDATION_ERROR */} +- `CUOPT_VALIDATION_ERROR` (`4`) +{/* /symbol */} + +{/* symbol: CUOPT_OUT_OF_MEMORY */} +- `CUOPT_OUT_OF_MEMORY` (`5`) +{/* /symbol */} + +{/* symbol: CUOPT_RUNTIME_ERROR */} +- `CUOPT_RUNTIME_ERROR` (`6`) +{/* /symbol */} + +## Optimization Problem + +An optimization problem is represented via a `cuOptOptimizationProblem`. + +{/* symbol: cuOptOptimizationProblem */} +**`typedef`** **`cuOptOptimizationProblem`** — A `cuOptOptimizationProblem` object contains a representation of an LP, MIP, QP, or QCQP. It is created by `cuOptCreateProblem`, `cuOptCreateRangedProblem`, or the quadratic create functions. Quadratic objectives and quadratic objectives and constraints may be set via `cuOptSetQuadraticObjective` and added via `cuOptAddQuadraticConstraint`. It is passed to `cuOptSolve` and destroyed with `cuOptDestroyProblem`. (`typedef void *`) +{/* /symbol */} + +Optimization problems can be created, loaded, or written via the following functions. + +{/* symbol: cuOptReadProblem */} +#### `cuOptReadProblem(const char * filename, cuOptOptimizationProblem * problem_ptr) -> cuopt_int_t` + +Read an optimization problem from an MPS, QPS, or LP file. + +**Parameters** + +- **`filename`** (`const char *`) `[in]` — The path to the MPS, QPS, or LP file. Must be a non-null, non-empty C string. +- **`problem_ptr`** (`cuOptOptimizationProblem *`) `[out]` — A non-null pointer to a cuOptOptimizationProblem. On output the problem will be created and initialized with the data from the input file. + +**Returns** A status code indicating success or failure. Returns CUOPT_INVALID_ARGUMENT if filename is null or empty, or if problem_ptr is null. +{/* /symbol */} + +{/* symbol: cuOptWriteProblem */} +#### `cuOptWriteProblem(cuOptOptimizationProblem problem, const char * filename, cuopt_int_t format) -> cuopt_int_t` + +Write an optimization problem to a file. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem to write. +- **`filename`** (`const char *`) `[in]` — The path to the output file. +- **`format`** (`cuopt_int_t`) `[in]` — The file format to use. Currently only CUOPT_FILE_FORMAT_MPS is supported. + +**Returns** A status code indicating success or failure. Returns CUOPT_INVALID_ARGUMENT if an unsupported format is specified. +{/* /symbol */} + +{/* symbol: cuOptCreateProblem */} +#### `cuOptCreateProblem(cuopt_int_t num_constraints, cuopt_int_t num_variables, cuopt_int_t objective_sense, cuopt_float_t objective_offset, const cuopt_float_t * objective_coefficients, const cuopt_int_t * constraint_matrix_row_offsets, const cuopt_int_t * constraint_matrix_column_indices, const cuopt_float_t * constraint_matrix_coefficent_values, const char * constraint_sense, const cuopt_float_t * rhs, const cuopt_float_t * lower_bounds, const cuopt_float_t * upper_bounds, const char * variable_types, cuOptOptimizationProblem * problem_ptr) -> cuopt_int_t` + +Create an optimization problem of the form. + +**Parameters** + +- **`num_constraints`** (`cuopt_int_t`) `[in]` — The number of constraints +- **`num_variables`** (`cuopt_int_t`) `[in]` — The number of variables +- **`objective_sense`** (`cuopt_int_t`) `[in]` — The objective sense (CUOPT_MINIMIZE for minimization or CUOPT_MAXIMIZE for maximization) +- **`objective_offset`** (`cuopt_float_t`) `[in]` — An offset to add to the linear objective +- **`objective_coefficients`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the coefficients of the linear objective +- **`constraint_matrix_row_offsets`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size num_constraints + 1. constraint_matrix_row_offsets[i] is the index of the first non-zero element of the i-th constraint in constraint_matrix_column_indices and constraint_matrix_coefficent_values. This is part of the compressed sparse row representation of the constraint matrix +- **`constraint_matrix_column_indices`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size constraint_matrix_row_offsets[num_constraints] containing the column indices of the non-zero elements of the constraint matrix. This is part of the compressed sparse row representation of the constraint matrix +- **`constraint_matrix_coefficent_values`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size constraint_matrix_row_offsets[num_constraints] containing the values of the non-zero elements of the constraint matrix. This is part of the compressed sparse row representation of the constraint matrix +- **`constraint_sense`** (`const char *`) `[in]` — A pointer to an array of type char of size num_constraints containing the sense of the constraints (CUOPT_LESS_THAN, CUOPT_GREATER_THAN, or CUOPT_EQUAL) +- **`rhs`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_constraints containing the right-hand side of the constraints +- **`lower_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the lower bounds of the variables +- **`upper_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the upper bounds of the variables +- **`variable_types`** (`const char *`) `[in]` — A pointer to an array of type char of size num_variables containing the types of the variables (CUOPT_CONTINUOUS, CUOPT_INTEGER, or CUOPT_SEMI_CONTINUOUS) +- **`problem_ptr`** (`cuOptOptimizationProblem *`) `[out]` — Pointer to store the created optimization problem + +**Returns** CUOPT_SUCCESS if successful, CUOPT_ERROR otherwise +{/* /symbol */} + +{/* symbol: cuOptCreateRangedProblem */} +#### `cuOptCreateRangedProblem(cuopt_int_t num_constraints, cuopt_int_t num_variables, cuopt_int_t objective_sense, cuopt_float_t objective_offset, const cuopt_float_t * objective_coefficients, const cuopt_int_t * constraint_matrix_row_offsets, const cuopt_int_t * constraint_matrix_column_indices, const cuopt_float_t * constraint_matrix_coefficients, const cuopt_float_t * constraint_lower_bounds, const cuopt_float_t * constraint_upper_bounds, const cuopt_float_t * variable_lower_bounds, const cuopt_float_t * variable_upper_bounds, const char * variable_types, cuOptOptimizationProblem * problem_ptr) -> cuopt_int_t` + +Create an optimization problem of the form *. + +**Parameters** + +- **`num_constraints`** (`cuopt_int_t`) `[in]` — The number of constraints. +- **`num_variables`** (`cuopt_int_t`) `[in]` — The number of variables. +- **`objective_sense`** (`cuopt_int_t`) `[in]` — The objective sense (CUOPT_MINIMIZE for minimization or CUOPT_MAXIMIZE for maximization) +- **`objective_offset`** (`cuopt_float_t`) `[in]` — An offset to add to the linear objective. +- **`objective_coefficients`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the coefficients of the linear objective. +- **`constraint_matrix_row_offsets`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size num_constraints + 1. constraint_matrix_row_offsets[i] is the index of the first non-zero element of the i-th constraint in constraint_matrix_column_indices and constraint_matrix_coefficients. +- **`constraint_matrix_column_indices`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size constraint_matrix_row_offsets[num_constraints] containing the column indices of the non-zero elements of the constraint matrix. +- **`constraint_matrix_coefficients`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size constraint_matrix_row_offsets[num_constraints] containing the values of the non-zero elements of the constraint matrix. +- **`constraint_lower_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_constraints containing the lower bounds of the constraints. +- **`constraint_upper_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_constraints containing the upper bounds of the constraints. +- **`variable_lower_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the lower bounds of the variables. +- **`variable_upper_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the upper bounds of the variables. +- **`variable_types`** (`const char *`) `[in]` — A pointer to an array of type char of size num_variables containing the types of the variables (CUOPT_CONTINUOUS, CUOPT_INTEGER, or CUOPT_SEMI_CONTINUOUS). +- **`problem_ptr`** (`cuOptOptimizationProblem *`) `[out]` — A pointer to a cuOptOptimizationProblem. On output the problem will be created and initialized with the provided data. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptCreateQuadraticProblem */} +#### `cuOptCreateQuadraticProblem(cuopt_int_t num_constraints, cuopt_int_t num_variables, cuopt_int_t objective_sense, cuopt_float_t objective_offset, const cuopt_float_t * objective_coefficients, const cuopt_int_t * quadratic_objective_matrix_row_offsets, const cuopt_int_t * quadratic_objective_matrix_column_indices, const cuopt_float_t * quadratic_objective_matrix_coefficent_values, const cuopt_int_t * constraint_matrix_row_offsets, const cuopt_int_t * constraint_matrix_column_indices, const cuopt_float_t * constraint_matrix_coefficent_values, const char * constraint_sense, const cuopt_float_t * rhs, const cuopt_float_t * lower_bounds, const cuopt_float_t * upper_bounds, cuOptOptimizationProblem * problem_ptr) -> cuopt_int_t` + +Create an optimization problem of the form. + + +Deprecated: Use `cuOptCreateProblem` to set up the linear problem, then `cuOptSetQuadraticObjective` to specify the quadratic objective terms. + + +**Parameters** + +- **`num_constraints`** (`cuopt_int_t`) `[in]` — The number of constraints +- **`num_variables`** (`cuopt_int_t`) `[in]` — The number of variables +- **`objective_sense`** (`cuopt_int_t`) `[in]` — The objective sense (CUOPT_MINIMIZE for minimization or CUOPT_MAXIMIZE for maximization) +- **`objective_offset`** (`cuopt_float_t`) `[in]` — An offset to add to the linear objective +- **`objective_coefficients`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the coefficients of the linear objective +- **`quadratic_objective_matrix_row_offsets`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size num_variables + 1. quadratic_objective_matrix_row_offsets[i] is the index of the first non-zero element of the i-th row of the quadratic objective matrix in quadratic_objective_matrix_column_indices and quadratic_objective_matrix_coefficent_values. This is part of the compressed sparse row representation of the quadratic objective matrix. +- **`quadratic_objective_matrix_column_indices`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size quadratic_objective_matrix_row_offsets[num_variables] containing the column indices of the non-zero elements of the quadratic objective matrix. This is part of the compressed sparse row representation of the quadratic objective matrix. +- **`quadratic_objective_matrix_coefficent_values`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size quadratic_objective_matrix_row_offsets[num_variables] containing the values of the non-zero elements of the quadratic objective matrix. +- **`constraint_matrix_row_offsets`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size num_constraints + 1. constraint_matrix_row_offsets[i] is the index of the first non-zero element of the i-th constraint in constraint_matrix_column_indices and constraint_matrix_coefficent_values. This is part of the compressed sparse row representation of the constraint matrix +- **`constraint_matrix_column_indices`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size constraint_matrix_row_offsets[num_constraints] containing the column indices of the non-zero elements of the constraint matrix. This is part of the compressed sparse row representation of the constraint matrix +- **`constraint_matrix_coefficent_values`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size constraint_matrix_row_offsets[num_constraints] containing the values of the non-zero elements of the constraint matrix. This is part of the compressed sparse row representation of the constraint matrix +- **`constraint_sense`** (`const char *`) `[in]` — A pointer to an array of type char of size num_constraints containing the sense of the constraints (CUOPT_LESS_THAN, CUOPT_GREATER_THAN, or CUOPT_EQUAL) +- **`rhs`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_constraints containing the right-hand side of the constraints +- **`lower_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the lower bounds of the variables +- **`upper_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the upper bounds of the variables +- **`problem_ptr`** (`cuOptOptimizationProblem *`) `[out]` — Pointer to store the created optimization problem + +**Returns** CUOPT_SUCCESS if successful, CUOPT_ERROR otherwise +{/* /symbol */} + +{/* symbol: cuOptCreateQuadraticRangedProblem */} +#### `cuOptCreateQuadraticRangedProblem(cuopt_int_t num_constraints, cuopt_int_t num_variables, cuopt_int_t objective_sense, cuopt_float_t objective_offset, const cuopt_float_t * objective_coefficients, const cuopt_int_t * quadratic_objective_matrix_row_offsets, const cuopt_int_t * quadratic_objective_matrix_column_indices, const cuopt_float_t * quadratic_objective_matrix_coefficent_values, const cuopt_int_t * constraint_matrix_row_offsets, const cuopt_int_t * constraint_matrix_column_indices, const cuopt_float_t * constraint_matrix_coefficients, const cuopt_float_t * constraint_lower_bounds, const cuopt_float_t * constraint_upper_bounds, const cuopt_float_t * variable_lower_bounds, const cuopt_float_t * variable_upper_bounds, cuOptOptimizationProblem * problem_ptr) -> cuopt_int_t` + +Create an optimization problem of the form *. + + +Deprecated: Use `cuOptCreateRangedProblem` to set up the linear problem, then `cuOptSetQuadraticObjective` to specify the quadratic objective terms. For QCQP models, use `cuOptAddQuadraticConstraint` for each quadratic constraint. + + +**Parameters** + +- **`num_constraints`** (`cuopt_int_t`) `[in]` — The number of constraints. +- **`num_variables`** (`cuopt_int_t`) `[in]` — The number of variables. +- **`objective_sense`** (`cuopt_int_t`) `[in]` — The objective sense (CUOPT_MINIMIZE for minimization or CUOPT_MAXIMIZE for maximization) +- **`objective_offset`** (`cuopt_float_t`) `[in]` — An offset to add to the linear objective. +- **`objective_coefficients`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the coefficients of the linear objective. +- **`quadratic_objective_matrix_row_offsets`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size num_variables + 1. quadratic_objective_matrix_row_offsets[i] is the index of the first non-zero element of the i-th row of the quadratic objective matrix in quadratic_objective_matrix_column_indices and quadratic_objective_matrix_coefficent_values. This is part of the compressed sparse row representation of the quadratic objective matrix. +- **`quadratic_objective_matrix_column_indices`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size quadratic_objective_matrix_row_offsets[num_variables] containing the column indices of the non-zero elements of the quadratic objective matrix. This is part of the compressed sparse row representation of the quadratic objective matrix. +- **`quadratic_objective_matrix_coefficent_values`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size quadratic_objective_matrix_row_offsets[num_variables] containing the values of the non-zero elements of the quadratic objective matrix. +- **`constraint_matrix_row_offsets`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size num_constraints + 1. constraint_matrix_row_offsets[i] is the index of the first non-zero element of the i-th constraint in constraint_matrix_column_indices and constraint_matrix_coefficients. +- **`constraint_matrix_column_indices`** (`const cuopt_int_t *`) `[in]` — A pointer to an array of type cuopt_int_t of size constraint_matrix_row_offsets[num_constraints] containing the column indices of the non-zero elements of the constraint matrix. +- **`constraint_matrix_coefficients`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size constraint_matrix_row_offsets[num_constraints] containing the values of the non-zero elements of the constraint matrix. +- **`constraint_lower_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_constraints containing the lower bounds of the constraints. +- **`constraint_upper_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_constraints containing the upper bounds of the constraints. +- **`variable_lower_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the lower bounds of the variables. +- **`variable_upper_bounds`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the upper bounds of the variables. +- **`problem_ptr`** (`cuOptOptimizationProblem *`) `[out]` — A pointer to a cuOptOptimizationProblem. On output the problem will be created and initialized with the provided data. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + + +`cuOptCreateQuadraticProblem` and `cuOptCreateQuadraticRangedProblem` are deprecated. +Prefer `cuOptCreateProblem` or `cuOptCreateRangedProblem` followed by `cuOptSetQuadraticObjective`. + + +For problems with quadratic objectives, first create a problem, and then use: + +{/* symbol: cuOptSetQuadraticObjective */} +#### `cuOptSetQuadraticObjective(cuOptOptimizationProblem problem, cuopt_int_t num_entries, const cuopt_int_t * row_index, const cuopt_int_t * col_index, const cuopt_float_t * coeff) -> cuopt_int_t` + +Set the quadratic objective term x^T Q x on an existing problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem created by `cuOptCreateProblem` or `cuOptCreateRangedProblem`. +- **`num_entries`** (`cuopt_int_t`) `[in]` — Number of non-zero entries in Q. +- **`row_index`** (`const cuopt_int_t *`) `[in]` — Array of length num_entries with row indices (0-based). +- **`col_index`** (`const cuopt_int_t *`) `[in]` — Array of length num_entries with column indices (0-based). +- **`coeff`** (`const cuopt_float_t *`) `[in]` — Array of length num_entries with matrix coefficients. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +For problems with quadratic constraints, first create a problem, and then use: + +{/* symbol: cuOptAddQuadraticConstraint */} +#### `cuOptAddQuadraticConstraint(cuOptOptimizationProblem problem, cuopt_int_t quad_num_entries, const cuopt_int_t * row_index, const cuopt_int_t * col_index, const cuopt_float_t * coeff, cuopt_int_t num_lin_entries, const cuopt_int_t * linear_index, const cuopt_float_t * linear_coeff, char sense, cuopt_float_t rhs) -> cuopt_int_t` + +Add a quadratic constraint x^T Q x + d^T x (<=, >=) rhs to an existing problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem created by `cuOptCreateProblem` or `cuOptCreateRangedProblem`. +- **`quad_num_entries`** (`cuopt_int_t`) `[in]` — Number of non-zero entries in the quadratic part. +- **`row_index`** (`const cuopt_int_t *`) `[in]` — Array of length quad_num_entries with row indices (0-based). +- **`col_index`** (`const cuopt_int_t *`) `[in]` — Array of length quad_num_entries with column indices (0-based). +- **`coeff`** (`const cuopt_float_t *`) `[in]` — Array of length quad_num_entries with quadratic matrix coefficients. +- **`num_lin_entries`** (`cuopt_int_t`) `[in]` — Number of non-zero entries in the linear part. +- **`linear_index`** (`const cuopt_int_t *`) `[in]` — Array of length num_lin_entries with variable indices (0-based). +- **`linear_coeff`** (`const cuopt_float_t *`) `[in]` — Array of length num_lin_entries with linear coefficients. +- **`sense`** (`char`) `[in]` — Constraint sense: `CUOPT_LESS_THAN` ('L') for <= or `CUOPT_GREATER_THAN` ('G') for >=. +- **`rhs`** (`cuopt_float_t`) `[in]` — Right-hand side of the constraint. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + + +Support for quadratic constraints is currently in **beta**. +`cuOptAddQuadraticConstraint` supports three types of quadratic constraints: +convex quadratic constraints of the form `x^T Q x + d^T x <= alpha` where `H=(Q+Q^T)/2` +is a symmetric positive semidefinite matrix; second-order cone constraints of the form +`sum x_i^2 <= x_0`, `x_0 >= 0`; and rotated second-order cone constraints. +Only `CUOPT_LESS_THAN` and `CUOPT_GREATER_THAN` sense is supported. + + +An optimization problem must be destroyed with the following function. + +{/* symbol: cuOptDestroyProblem */} +#### `cuOptDestroyProblem(cuOptOptimizationProblem * problem_ptr)` + +Destroy an optimization problem. + +**Parameters** + +- **`problem_ptr`** (`cuOptOptimizationProblem *`) `[inout]` — A pointer to a cuOptOptimizationProblem. On output the problem will be destroyed, and the pointer will be set to NULL. +{/* /symbol */} + +## Objective Sense Constants + +These constants are used to define the objective sense in the `cuOptCreateProblem` and `cuOptCreateRangedProblem` functions. + +{/* symbol: CUOPT_MINIMIZE */} +- `CUOPT_MINIMIZE` (`1`) +{/* /symbol */} + +{/* symbol: CUOPT_MAXIMIZE */} +- `CUOPT_MAXIMIZE` (`-1`) +{/* /symbol */} + +## Constraint Sense Constants + +These constants are used to define the constraint sense in the `cuOptCreateProblem` and `cuOptCreateRangedProblem` functions. + +{/* symbol: CUOPT_LESS_THAN */} +- `CUOPT_LESS_THAN` (`'L'`) +{/* /symbol */} + +{/* symbol: CUOPT_GREATER_THAN */} +- `CUOPT_GREATER_THAN` (`'G'`) +{/* /symbol */} + +{/* symbol: CUOPT_EQUAL */} +- `CUOPT_EQUAL` (`'E'`) +{/* /symbol */} + +## Variable Type Constants + +These constants are used to define the variable type in the `cuOptCreateProblem` and `cuOptCreateRangedProblem` functions. + +{/* symbol: CUOPT_CONTINUOUS */} +- `CUOPT_CONTINUOUS` (`'C'`) +{/* /symbol */} + +{/* symbol: CUOPT_INTEGER */} +- `CUOPT_INTEGER` (`'I'`) +{/* /symbol */} + +{/* symbol: CUOPT_SEMI_CONTINUOUS */} +- `CUOPT_SEMI_CONTINUOUS` (`'S'`) +{/* /symbol */} + +## Infinity Constant + +This constant may be used to represent infinity in the `cuOptCreateProblem` and `cuOptCreateRangedProblem` functions. + +{/* symbol: CUOPT_INFINITY */} +- `CUOPT_INFINITY` (`INFINITY`) +{/* /symbol */} + +## File Format Constants + +These constants are used to specify the output file format in `cuOptWriteProblem`. + +{/* symbol: CUOPT_FILE_FORMAT_MPS */} +- `CUOPT_FILE_FORMAT_MPS` (`0`) +{/* /symbol */} + +## Querying an Optimization Problem + +The following functions may be used to get information about a `cuOptOptimizationProblem`. + +{/* symbol: cuOptGetNumConstraints */} +#### `cuOptGetNumConstraints(cuOptOptimizationProblem problem, cuopt_int_t * num_constraints_ptr) -> cuopt_int_t` + +Get the number of constraints of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`num_constraints_ptr`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that will contain the number of constraints on output. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetNumVariables */} +#### `cuOptGetNumVariables(cuOptOptimizationProblem problem, cuopt_int_t * num_variables_ptr) -> cuopt_int_t` + +Get the number of variables of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`num_variables_ptr`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that will contain the number of variables on output. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetObjectiveSense */} +#### `cuOptGetObjectiveSense(cuOptOptimizationProblem problem, cuopt_int_t * objective_sense_ptr) -> cuopt_int_t` + +Get the objective sense of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`objective_sense_ptr`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that on output will contain the objective sense. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetObjectiveOffset */} +#### `cuOptGetObjectiveOffset(cuOptOptimizationProblem problem, cuopt_float_t * objective_offset_ptr) -> cuopt_int_t` + +Get the objective offset of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`objective_offset_ptr`** (`cuopt_float_t *`) `[out]` — A pointer to a cuopt_float_t that on output will contain the objective offset. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetObjectiveCoefficients */} +#### `cuOptGetObjectiveCoefficients(cuOptOptimizationProblem problem, cuopt_float_t * objective_coefficients_ptr) -> cuopt_int_t` + +Get the objective coefficients of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`objective_coefficients_ptr`** (`cuopt_float_t *`) `[out]` — A pointer to an array of type cuopt_float_t of size num_variables that on output will contain the objective coefficients. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetNumNonZeros */} +#### `cuOptGetNumNonZeros(cuOptOptimizationProblem problem, cuopt_int_t * num_non_zeros_ptr) -> cuopt_int_t` + +Get the number of non-zero elements in the constraint matrix of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`num_non_zeros_ptr`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that on output will contain the number of non-zeros in the constraint matrix. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetConstraintMatrix */} +#### `cuOptGetConstraintMatrix(cuOptOptimizationProblem problem, cuopt_int_t * constraint_matrix_row_offsets_ptr, cuopt_int_t * constraint_matrix_column_indices_ptr, cuopt_float_t * constraint_matrix_coefficients_ptr) -> cuopt_int_t` + +Get the constraint matrix of an optimization problem in compressed sparse row format. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`constraint_matrix_row_offsets_ptr`** (`cuopt_int_t *`) `[out]` — A pointer to an array of type cuopt_int_t of size num_constraints + 1 that on output will contain the row offsets of the constraint matrix. +- **`constraint_matrix_column_indices_ptr`** (`cuopt_int_t *`) `[out]` — A pointer to an array of type cuopt_int_t of size equal to the number of nonzeros that on output will contain the column indices of the non-zero entries of the constraint matrix. +- **`constraint_matrix_coefficients_ptr`** (`cuopt_float_t *`) `[out]` — A pointer to an array of type cuopt_float_t of size equal to the number of nonzeros that on output will contain the coefficients of the non-zero entries of the constraint matrix. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetConstraintSense */} +#### `cuOptGetConstraintSense(cuOptOptimizationProblem problem, char * constraint_sense_ptr) -> cuopt_int_t` + +Get the constraint sense of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`constraint_sense_ptr`** (`char *`) `[out]` — A pointer to an array of type char of size num_constraints that on output will contain the sense of the constraints. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetConstraintRightHandSide */} +#### `cuOptGetConstraintRightHandSide(cuOptOptimizationProblem problem, cuopt_float_t * rhs_ptr) -> cuopt_int_t` + +Get the right-hand side of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`rhs_ptr`** (`cuopt_float_t *`) `[out]` — A pointer to an array of type cuopt_float_t of size num_constraints that on output will contain the right-hand side of the constraints. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetConstraintLowerBounds */} +#### `cuOptGetConstraintLowerBounds(cuOptOptimizationProblem problem, cuopt_float_t * lower_bounds_ptr) -> cuopt_int_t` + +Get the lower bounds of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`lower_bounds_ptr`** (`cuopt_float_t *`) `[out]` — A pointer to an array of type cuopt_float_t of size num_constraints that on output will contain the lower bounds of the constraints. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetConstraintUpperBounds */} +#### `cuOptGetConstraintUpperBounds(cuOptOptimizationProblem problem, cuopt_float_t * upper_bounds_ptr) -> cuopt_int_t` + +Get the upper bounds of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`upper_bounds_ptr`** (`cuopt_float_t *`) `[out]` — A pointer to an array of type cuopt_float_t of size num_constraints that on output will contain the upper bounds of the constraints. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetVariableLowerBounds */} +#### `cuOptGetVariableLowerBounds(cuOptOptimizationProblem problem, cuopt_float_t * lower_bounds_ptr) -> cuopt_int_t` + +Get the lower bounds of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`lower_bounds_ptr`** (`cuopt_float_t *`) `[out]` — A pointer to an array of type cuopt_float_t of size num_variables that on output will contain the lower bounds of the variables. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetVariableUpperBounds */} +#### `cuOptGetVariableUpperBounds(cuOptOptimizationProblem problem, cuopt_float_t * upper_bounds_ptr) -> cuopt_int_t` + +Get the upper bounds of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`upper_bounds_ptr`** (`cuopt_float_t *`) `[out]` — A pointer to an array of type cuopt_float_t of size num_variables that on output will contain the upper bounds of the variables. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetVariableTypes */} +#### `cuOptGetVariableTypes(cuOptOptimizationProblem problem, char * variable_types_ptr) -> cuopt_int_t` + +Get the variable types of an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`variable_types_ptr`** (`char *`) `[out]` — A pointer to an array of type char of size num_variables that on output will contain the types of the variables (CUOPT_CONTINUOUS, CUOPT_INTEGER, or CUOPT_SEMI_CONTINUOUS). + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptIsMIP */} +#### `cuOptIsMIP(cuOptOptimizationProblem problem, cuopt_int_t * is_mip_ptr) -> cuopt_int_t` + +Check if an optimization problem is a mixed integer programming problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`is_mip_ptr`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that on output will be 0 if the problem contains only continuous variables, or 1 if the problem contains integer variables. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +## Solver Settings + +Settings are used to configure the LP/MIP solvers. +All settings are stored in a `cuOptSolverSettings` object. + +{/* symbol: cuOptSolverSettings */} +**`typedef`** **`cuOptSolverSettings`** — A `cuOptSolverSettings` object contains parameter settings and other information for an LP or MIP solve. It is created by `cuOptCreateSolverSettings`. It is passed to `cuOptSolve`. It should be destroyed using `cuOptDestroySolverSettings`. (`typedef void *`) +{/* /symbol */} + +A `cuOptSolverSettings` object is created with `cuOptCreateSolverSettings`. + +{/* symbol: cuOptCreateSolverSettings */} +#### `cuOptCreateSolverSettings(cuOptSolverSettings * settings_ptr) -> cuopt_int_t` + +Create a solver settings object. + +**Parameters** + +- **`settings_ptr`** (`cuOptSolverSettings *`) `[out]` — A pointer to a cuOptSolverSettings object. On output the solver settings will be created and initialized. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +When you are done with a solve you should destroy a `cuOptSolverSettings` object with: + +{/* symbol: cuOptDestroySolverSettings */} +#### `cuOptDestroySolverSettings(cuOptSolverSettings * settings_ptr)` + +Destroy a solver settings object. + +**Parameters** + +- **`settings_ptr`** (`cuOptSolverSettings *`) `[inout]` — A pointer to a cuOptSolverSettings object. On output the solver settings will be destroyed and the pointer will be set to NULL. +{/* /symbol */} + +## Setting Parameters + +The following functions are used to set and get parameters. +You can find more details on the available parameters in the +[Convex Optimization Settings](../../convex-settings) section. + +{/* symbol: cuOptSetParameter */} +#### `cuOptSetParameter(cuOptSolverSettings settings, const char * parameter_name, const char * parameter_value) -> cuopt_int_t` + +Set a parameter of a solver settings object. + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`parameter_name`** (`const char *`) `[in]` — The name of the parameter to set. +- **`parameter_value`** (`const char *`) `[in]` — The value of the parameter to set. +{/* /symbol */} + +{/* symbol: cuOptGetParameter */} +#### `cuOptGetParameter(cuOptSolverSettings settings, const char * parameter_name, cuopt_int_t parameter_value_size, char * parameter_value) -> cuopt_int_t` + +Get a parameter of a solver settings object. + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`parameter_name`** (`const char *`) `[in]` — The name of the parameter to get. +- **`parameter_value_size`** (`cuopt_int_t`) `[in]` — The size of the parameter value buffer. +- **`parameter_value`** (`char *`) `[out]` — A pointer to an array of characters that on output will contain the value of the parameter. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptSetIntegerParameter */} +#### `cuOptSetIntegerParameter(cuOptSolverSettings settings, const char * parameter_name, cuopt_int_t parameter_value) -> cuopt_int_t` + +Set an integer parameter of a solver settings object. + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`parameter_name`** (`const char *`) `[in]` — The name of the parameter to set. +- **`parameter_value`** (`cuopt_int_t`) `[in]` — The value of the parameter to set. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetIntegerParameter */} +#### `cuOptGetIntegerParameter(cuOptSolverSettings settings, const char * parameter_name, cuopt_int_t * parameter_value) -> cuopt_int_t` + +Get an integer parameter of a solver settings object. + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`parameter_name`** (`const char *`) `[in]` — The name of the parameter to get. +- **`parameter_value`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that on output will contain the value of the parameter. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptSetFloatParameter */} +#### `cuOptSetFloatParameter(cuOptSolverSettings settings, const char * parameter_name, cuopt_float_t parameter_value) -> cuopt_int_t` + +Set a float parameter of a solver settings object. + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`parameter_name`** (`const char *`) `[in]` — The name of the parameter to set. +- **`parameter_value`** (`cuopt_float_t`) `[in]` — The value of the parameter to set. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetFloatParameter */} +#### `cuOptGetFloatParameter(cuOptSolverSettings settings, const char * parameter_name, cuopt_float_t * parameter_value) -> cuopt_int_t` + +Get a float parameter of a solver settings object. + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`parameter_name`** (`const char *`) `[in]` — The name of the parameter to get. +- **`parameter_value`** (`cuopt_float_t *`) `[out]` — A pointer to a cuopt_float_t that on output will contain the value of the parameter. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +## Parameter Constants + +These constants are used as parameter names in `cuOptSetParameter`, `cuOptGetParameter`, and similar functions. +For more details, see the [Convex Optimization Settings](../../convex-settings) and [MIP Settings](../../mip-settings) sections. + +{/* symbol: CUOPT_ABSOLUTE_DUAL_TOLERANCE */} +- `CUOPT_ABSOLUTE_DUAL_TOLERANCE` (`"absolute_dual_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_RELATIVE_DUAL_TOLERANCE */} +- `CUOPT_RELATIVE_DUAL_TOLERANCE` (`"relative_dual_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_ABSOLUTE_PRIMAL_TOLERANCE */} +- `CUOPT_ABSOLUTE_PRIMAL_TOLERANCE` (`"absolute_primal_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_RELATIVE_PRIMAL_TOLERANCE */} +- `CUOPT_RELATIVE_PRIMAL_TOLERANCE` (`"relative_primal_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_ABSOLUTE_GAP_TOLERANCE */} +- `CUOPT_ABSOLUTE_GAP_TOLERANCE` (`"absolute_gap_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_RELATIVE_GAP_TOLERANCE */} +- `CUOPT_RELATIVE_GAP_TOLERANCE` (`"relative_gap_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_INFEASIBILITY_DETECTION */} +- `CUOPT_INFEASIBILITY_DETECTION` (`"infeasibility_detection"`) +{/* /symbol */} + +{/* symbol: CUOPT_STRICT_INFEASIBILITY */} +- `CUOPT_STRICT_INFEASIBILITY` (`"strict_infeasibility"`) +{/* /symbol */} + +{/* symbol: CUOPT_PRIMAL_INFEASIBLE_TOLERANCE */} +- `CUOPT_PRIMAL_INFEASIBLE_TOLERANCE` (`"primal_infeasible_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_DUAL_INFEASIBLE_TOLERANCE */} +- `CUOPT_DUAL_INFEASIBLE_TOLERANCE` (`"dual_infeasible_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_ITERATION_LIMIT */} +- `CUOPT_ITERATION_LIMIT` (`"iteration_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_TIME_LIMIT */} +- `CUOPT_TIME_LIMIT` (`"time_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_SOLVER_MODE */} +- `CUOPT_PDLP_SOLVER_MODE` (`"pdlp_solver_mode"`) +{/* /symbol */} + +{/* symbol: CUOPT_METHOD */} +- `CUOPT_METHOD` (`"method"`) +{/* /symbol */} + +{/* symbol: CUOPT_PER_CONSTRAINT_RESIDUAL */} +- `CUOPT_PER_CONSTRAINT_RESIDUAL` (`"per_constraint_residual"`) +{/* /symbol */} + +{/* symbol: CUOPT_SAVE_BEST_PRIMAL_SO_FAR */} +- `CUOPT_SAVE_BEST_PRIMAL_SO_FAR` (`"save_best_primal_so_far"`) +{/* /symbol */} + +{/* symbol: CUOPT_FIRST_PRIMAL_FEASIBLE */} +- `CUOPT_FIRST_PRIMAL_FEASIBLE` (`"first_primal_feasible"`) +{/* /symbol */} + +{/* symbol: CUOPT_LOG_FILE */} +- `CUOPT_LOG_FILE` (`"log_file"`) +{/* /symbol */} + +{/* symbol: CUOPT_PRESOLVE */} +- `CUOPT_PRESOLVE` (`"presolve"`) +{/* /symbol */} + +{/* symbol: CUOPT_LOG_TO_CONSOLE */} +- `CUOPT_LOG_TO_CONSOLE` (`"log_to_console"`) +{/* /symbol */} + +{/* symbol: CUOPT_CROSSOVER */} +- `CUOPT_CROSSOVER` (`"crossover"`) +{/* /symbol */} + +{/* symbol: CUOPT_FOLDING */} +- `CUOPT_FOLDING` (`"folding"`) +{/* /symbol */} + +{/* symbol: CUOPT_AUGMENTED */} +- `CUOPT_AUGMENTED` (`"augmented"`) +{/* /symbol */} + +{/* symbol: CUOPT_DUALIZE */} +- `CUOPT_DUALIZE` (`"dualize"`) +{/* /symbol */} + +{/* symbol: CUOPT_ORDERING */} +- `CUOPT_ORDERING` (`"ordering"`) +{/* /symbol */} + +{/* symbol: CUOPT_ELIMINATE_DENSE_COLUMNS */} +- `CUOPT_ELIMINATE_DENSE_COLUMNS` (`"eliminate_dense_columns"`) +{/* /symbol */} + +{/* symbol: CUOPT_CUDSS_DETERMINISTIC */} +- `CUOPT_CUDSS_DETERMINISTIC` (`"cudss_deterministic"`) +{/* /symbol */} + +{/* symbol: CUOPT_BARRIER_DUAL_INITIAL_POINT */} +- `CUOPT_BARRIER_DUAL_INITIAL_POINT` (`"barrier_dual_initial_point"`) +{/* /symbol */} + +{/* symbol: CUOPT_BARRIER_ITERATIVE_REFINEMENT */} +- `CUOPT_BARRIER_ITERATIVE_REFINEMENT` (`"barrier_iterative_refinement"`) +{/* /symbol */} + +{/* symbol: CUOPT_BARRIER_STEP_SCALE */} +- `CUOPT_BARRIER_STEP_SCALE` (`"barrier_step_scale"`) +{/* /symbol */} + +{/* symbol: CUOPT_DUAL_POSTSOLVE */} +- `CUOPT_DUAL_POSTSOLVE` (`"dual_postsolve"`) +{/* /symbol */} + +{/* symbol: CUOPT_SOLUTION_FILE */} +- `CUOPT_SOLUTION_FILE` (`"solution_file"`) +{/* /symbol */} + +{/* symbol: CUOPT_NUM_CPU_THREADS */} +- `CUOPT_NUM_CPU_THREADS` (`"num_cpu_threads"`) +{/* /symbol */} + +{/* symbol: CUOPT_NUM_GPUS */} +- `CUOPT_NUM_GPUS` (`"num_gpus"`) +{/* /symbol */} + +{/* symbol: CUOPT_USER_PROBLEM_FILE */} +- `CUOPT_USER_PROBLEM_FILE` (`"user_problem_file"`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_PRECISION */} +- `CUOPT_PDLP_PRECISION` (`"pdlp_precision"`) +{/* /symbol */} + +## PDLP Solver Mode Constants + +These constants are used to configure `CUOPT_PDLP_SOLVER_MODE` via `cuOptSetIntegerParameter`. + +{/* symbol: CUOPT_PDLP_SOLVER_MODE_STABLE1 */} +- `CUOPT_PDLP_SOLVER_MODE_STABLE1` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_SOLVER_MODE_STABLE2 */} +- `CUOPT_PDLP_SOLVER_MODE_STABLE2` (`1`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_SOLVER_MODE_STABLE3 */} +- `CUOPT_PDLP_SOLVER_MODE_STABLE3` (`4`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_SOLVER_MODE_METHODICAL1 */} +- `CUOPT_PDLP_SOLVER_MODE_METHODICAL1` (`2`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_SOLVER_MODE_FAST1 */} +- `CUOPT_PDLP_SOLVER_MODE_FAST1` (`3`) +{/* /symbol */} + +## PDLP Precision Constants + +These constants are used to configure `CUOPT_PDLP_PRECISION` via `cuOptSetIntegerParameter`. + +{/* symbol: CUOPT_PDLP_DEFAULT_PRECISION */} +- `CUOPT_PDLP_DEFAULT_PRECISION` (`-1`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_SINGLE_PRECISION */} +- `CUOPT_PDLP_SINGLE_PRECISION` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_DOUBLE_PRECISION */} +- `CUOPT_PDLP_DOUBLE_PRECISION` (`1`) +{/* /symbol */} + +{/* symbol: CUOPT_PDLP_MIXED_PRECISION */} +- `CUOPT_PDLP_MIXED_PRECISION` (`2`) +{/* /symbol */} + +## Method Constants + +These constants are used to configure `CUOPT_METHOD` via `cuOptSetIntegerParameter`. + +{/* symbol: CUOPT_METHOD_CONCURRENT */} +- `CUOPT_METHOD_CONCURRENT` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_METHOD_PDLP */} +- `CUOPT_METHOD_PDLP` (`1`) +{/* /symbol */} + +{/* symbol: CUOPT_METHOD_DUAL_SIMPLEX */} +- `CUOPT_METHOD_DUAL_SIMPLEX` (`2`) +{/* /symbol */} + +{/* symbol: CUOPT_METHOD_BARRIER */} +- `CUOPT_METHOD_BARRIER` (`3`) +{/* /symbol */} + +{/* symbol: CUOPT_METHOD_UNSET */} +- `CUOPT_METHOD_UNSET` (`4`) +{/* /symbol */} + +## Barrier Iterative Refinement Constants + +These constants are used to configure `CUOPT_BARRIER_ITERATIVE_REFINEMENT` via `cuOptSetIntegerParameter`. + +{/* symbol: CUOPT_BARRIER_ITERATIVE_REFINEMENT_OFF */} +- `CUOPT_BARRIER_ITERATIVE_REFINEMENT_OFF` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_BARRIER_ITERATIVE_REFINEMENT_ON */} +- `CUOPT_BARRIER_ITERATIVE_REFINEMENT_ON` (`1`) +{/* /symbol */} + +## Presolve Constants + +These constants are used to configure `CUOPT_PRESOLVE` via `cuOptSetIntegerParameter`. + +{/* symbol: CUOPT_PRESOLVE_DEFAULT */} +- `CUOPT_PRESOLVE_DEFAULT` (`-1`) +{/* /symbol */} + +{/* symbol: CUOPT_PRESOLVE_OFF */} +- `CUOPT_PRESOLVE_OFF` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_PRESOLVE_PAPILO */} +- `CUOPT_PRESOLVE_PAPILO` (`1`) +{/* /symbol */} + +{/* symbol: CUOPT_PRESOLVE_PSLP */} +- `CUOPT_PRESOLVE_PSLP` (`2`) +{/* /symbol */} + +## Warm Start + +For LP problems solved with PDLP, primal and dual warm start vectors may be provided. + +{/* symbol: cuOptSetInitialPrimalSolution */} +#### `cuOptSetInitialPrimalSolution(cuOptSolverSettings settings, const cuopt_float_t * primal_solution, cuopt_int_t num_variables) -> cuopt_int_t` + +Set the initial primal solution for an LP solve. + + +This function is only supported for PDLP. All pointer arguments (primal_solution) refer to host memory. + + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`primal_solution`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the initial primal values. +- **`num_variables`** (`cuopt_int_t`) `[in]` — The number of variables (size of the primal_solution array). + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptSetInitialDualSolution */} +#### `cuOptSetInitialDualSolution(cuOptSolverSettings settings, const cuopt_float_t * dual_solution, cuopt_int_t num_constraints) -> cuopt_int_t` + +Set the initial dual solution for an LP solve. + + +This function is only supported for PDLP. All pointer arguments (dual_solution) refer to host memory. + + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`dual_solution`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_constraints containing the initial dual values. +- **`num_constraints`** (`cuopt_int_t`) `[in]` — The number of constraints (size of the dual_solution array). + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +For MIP warm start (MIP starts), see [MIP C API](mip-c-api). + +## Solving + +LP and MIP solves are performed by calling the `cuOptSolve` function. + +{/* symbol: cuOptSolve */} +#### `cuOptSolve(cuOptOptimizationProblem problem, cuOptSolverSettings settings, cuOptSolution * solution_ptr) -> cuopt_int_t` + +Solve an optimization problem. + +**Parameters** + +- **`problem`** (`cuOptOptimizationProblem`) `[in]` — The optimization problem. +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings. +- **`solution_ptr`** (`cuOptSolution *`) `[out]` — A pointer to a cuOptSolution object. On output the solution will be created. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +## Solution + +The output of a solve is a `cuOptSolution` object. + +{/* symbol: cuOptSolution */} +**`typedef`** **`cuOptSolution`** — A `cuOptSolution` object contains the solution to an LP or MIP. It is created by `cuOptSolve`. It should be destroyed using `cuOptDestroySolution`. (`typedef void *`) +{/* /symbol */} + +The following functions may be used to access information from a `cuOptSolution`. + +{/* symbol: cuOptGetTerminationStatus */} +#### `cuOptGetTerminationStatus(cuOptSolution solution, cuopt_int_t * termination_status_ptr) -> cuopt_int_t` + +Get the termination reason of an optimization problem. + +**Parameters** + +- **`solution`** (`cuOptSolution`) `[in]` — The solution object. +- **`termination_status_ptr`** (`cuopt_int_t *`) `[out]` — A pointer to a cuopt_int_t that on output will contain the termination status. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetErrorStatus */} +#### `cuOptGetErrorStatus(cuOptSolution solution, cuopt_int_t * error_status_ptr) -> cuopt_int_t` +{/* /symbol */} + +{/* symbol: cuOptGetErrorString */} +#### `cuOptGetErrorString(cuOptSolution solution, char * error_string_ptr, cuopt_int_t error_string_size) -> cuopt_int_t` +{/* /symbol */} + +{/* symbol: cuOptGetPrimalSolution */} +#### `cuOptGetPrimalSolution(cuOptSolution solution, cuopt_float_t * solution_values) -> cuopt_int_t` +{/* /symbol */} + +{/* symbol: cuOptGetObjectiveValue */} +#### `cuOptGetObjectiveValue(cuOptSolution solution, cuopt_float_t * objective_value_ptr) -> cuopt_int_t` + +Get the objective value of an optimization problem. + +**Parameters** + +- **`solution`** (`cuOptSolution`) `[in]` — The solution object. +- **`objective_value_ptr`** (`cuopt_float_t *`) `[inout]` — A pointer to a cuopt_float_t that will contain the objective value. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetSolveTime */} +#### `cuOptGetSolveTime(cuOptSolution solution, cuopt_float_t * solve_time_ptr) -> cuopt_int_t` + +Get the solve time of an optimization problem. + +**Parameters** + +- **`solution`** (`cuOptSolution`) `[in]` — The solution object. +- **`solve_time_ptr`** (`cuopt_float_t *`) `[inout]` — A pointer to a cuopt_float_t that will contain the solve time. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetMIPGap */} +#### `cuOptGetMIPGap(cuOptSolution solution, cuopt_float_t * mip_gap_ptr) -> cuopt_int_t` + +Get the relative MIP gap of an optimization problem. + +**Parameters** + +- **`solution`** (`cuOptSolution`) `[in]` — The solution object. +- **`mip_gap_ptr`** (`cuopt_float_t *`) `[inout]` — A pointer to a cuopt_float_t that will contain the relative MIP gap. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetSolutionBound */} +#### `cuOptGetSolutionBound(cuOptSolution solution, cuopt_float_t * solution_bound_ptr) -> cuopt_int_t` + +Get the solution bound of an optimization problem. + +**Parameters** + +- **`solution`** (`cuOptSolution`) `[in]` — The solution object. +- **`solution_bound_ptr`** (`cuopt_float_t *`) `[inout]` — A pointer to a cuopt_float_t that will contain the solution bound. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetDualSolution */} +#### `cuOptGetDualSolution(cuOptSolution solution, cuopt_float_t * dual_solution_ptr) -> cuopt_int_t` + +Get the dual solution of an optimization problem. + +**Parameters** + +- **`solution`** (`cuOptSolution`) `[in]` — The solution object. +- **`dual_solution_ptr`** (`cuopt_float_t *`) `[inout]` — A pointer to an array of type cuopt_float_t of size num_constraints that will contain the dual solution. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetDualObjectiveValue */} +#### `cuOptGetDualObjectiveValue(cuOptSolution solution, cuopt_float_t * dual_objective_value_ptr) -> cuopt_int_t` + +Get the dual objective value of an optimization problem. + +**Parameters** + +- **`solution`** (`cuOptSolution`) `[in]` — The solution object. +- **`dual_objective_value_ptr`** (`cuopt_float_t *`) `[inout]` — A pointer to a cuopt_float_t that will contain the dual objective value. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptGetReducedCosts */} +#### `cuOptGetReducedCosts(cuOptSolution solution, cuopt_float_t * reduced_cost_ptr) -> cuopt_int_t` + +Get the reduced costs of an optimization problem. + +**Parameters** + +- **`solution`** (`cuOptSolution`) `[in]` — The solution object. +- **`reduced_cost_ptr`** (`cuopt_float_t *`) `[inout]` — A pointer to an array of type cuopt_float_t of size num_variables that will contain the reduced cost. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +When you are finished with a `cuOptSolution` object you should destroy it with: + +{/* symbol: cuOptDestroySolution */} +#### `cuOptDestroySolution(cuOptSolution * solution_ptr)` + +Destroy a solution object. + +**Parameters** + +- **`solution_ptr`** (`cuOptSolution *`) `[inout]` — A pointer to a cuOptSolution object. On output the solution will be destroyed and the pointer will be set to NULL. +{/* /symbol */} + +## Termination Status Constants + +These constants define the termination status received from `cuOptGetTerminationStatus`. + +{/* symbol: CUOPT_TERMINATION_STATUS_NO_TERMINATION */} +- `CUOPT_TERMINATION_STATUS_NO_TERMINATION` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_OPTIMAL */} +- `CUOPT_TERMINATION_STATUS_OPTIMAL` (`1`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_INFEASIBLE */} +- `CUOPT_TERMINATION_STATUS_INFEASIBLE` (`2`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_UNBOUNDED */} +- `CUOPT_TERMINATION_STATUS_UNBOUNDED` (`3`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_ITERATION_LIMIT */} +- `CUOPT_TERMINATION_STATUS_ITERATION_LIMIT` (`4`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_TIME_LIMIT */} +- `CUOPT_TERMINATION_STATUS_TIME_LIMIT` (`5`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR */} +- `CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR` (`6`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE */} +- `CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE` (`7`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND */} +- `CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND` (`8`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_CONCURRENT_LIMIT */} +- `CUOPT_TERMINATION_STATUS_CONCURRENT_LIMIT` (`9`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_WORK_LIMIT */} +- `CUOPT_TERMINATION_STATUS_WORK_LIMIT` (`10`) +{/* /symbol */} + +{/* symbol: CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE */} +- `CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE` (`11`) +{/* /symbol */} diff --git a/fern/docs/pages/cuopt-c/convex/convex-examples.mdx b/fern/docs/pages/cuopt-c/convex/convex-examples.mdx new file mode 100644 index 0000000000..447ec03b69 --- /dev/null +++ b/fern/docs/pages/cuopt-c/convex/convex-examples.mdx @@ -0,0 +1,1599 @@ +--- +title: "Convex Optimization C API Examples" +--- + +More details on the API can be found in [C API](convex-c-api). + +**Build prerequisites:** Set `CUOPT_INCLUDE` and `CUOPT_LIB` to your cuOpt include and library paths, then use the provided `Makefile` in the examples directory: + +```bash +make INCLUDE_PATH=$CUOPT_INCLUDE LIBCUOPT_LIBRARY_PATH=$CUOPT_LIB all +``` + +If you built cuOpt locally, `CUOPT_INCLUDE=cpp/include` and `CUOPT_LIB=cpp/build`. + +## LP Example With Data + +Builds and solves a small LP directly with the C API using `cuOptCreateProblem`. + +{/* embed: examples/simple_lp_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* + * Simple LP C API Example + * + * This example demonstrates how to use the cuOpt C API for linear programming. + * + * Problem: + * Minimize: -0.2*x1 + 0.1*x2 + * Subject to: + * 3.0*x1 + 4.0*x2 <= 5.4 + * 2.7*x1 + 10.1*x2 <= 4.9 + * x1, x2 >= 0 + * + * Expected Output: + * Termination status: Optimal (1) + * Solve time: 0.000013 seconds + * Objective value: -0.360000 + * x1 = 1.800000 + * x2 = 0.000000 + * + * Build: + * gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o simple_lp_example simple_lp_example.c -lcuopt + * + * Run: + * ./simple_lp_example + */ + +// Include the cuOpt linear programming solver header +#include +#include +#include + +// Convert termination status to string +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: + return "Primal feasible"; + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: + return "Feasible found"; + default: + return "Unknown"; + } +} + +// Test simple LP problem +cuopt_int_t test_simple_lp() +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + /* Solve the following LP: + minimize -0.2*x1 + 0.1*x2 + subject to: + 3.0*x1 + 4.0*x2 <= 5.4 + 2.7*x1 + 10.1*x2 <= 4.9 + x1, x2 >= 0 + */ + + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 2; + cuopt_int_t nnz = 4; + + // CSR format constraint matrix + // https://docs.nvidia.com/nvpl/latest/sparse/storage_format/sparse_matrix.html#compressed-sparse-row-csr + // From the constraints: + // 3.0*x1 + 4.0*x2 <= 5.4 + // 2.7*x1 + 10.1*x2 <= 4.9 + cuopt_int_t row_offsets[] = {0, 2, 4}; + cuopt_int_t column_indices[] = {0, 1, 0, 1}; + cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; + + // Objective coefficients + // From the objective function: minimize -0.2*x1 + 0.1*x2 + // -0.2 is the coefficient of x1 + // 0.1 is the coefficient of x2 + cuopt_float_t objective_coefficients[] = {-0.2, 0.1}; + + // Constraint bounds + // From the constraints: + // 3.0*x1 + 4.0*x2 <= 5.4 + // 2.7*x1 + 10.1*x2 <= 4.9 + cuopt_float_t constraint_upper_bounds[] = {5.4, 4.9}; + cuopt_float_t constraint_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY}; + + // Variable bounds + // From the constraints: + // x1, x2 >= 0 + cuopt_float_t var_lower_bounds[] = {0.0, 0.0}; + cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + + // Variable types (continuous) + // From the constraints: + // x1, x2 >= 0 + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + + printf("Creating and solving simple LP problem...\n"); + + // Create the problem + status = cuOptCreateRangedProblem(num_constraints, + num_variables, + CUOPT_MINIMIZE, + 0.0, // objective offset + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_lower_bounds, + constraint_upper_bounds, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + goto DONE; + } + + // Create solver settings + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + // Set solver parameters + status = cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting optimality tolerance: %d\n", status); + goto DONE; + } + + // Solve the problem + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + // Get solution information + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + + // Print results + printf("\nResults:\n"); + printf("--------\n"); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + printf("Objective value: %f\n", objective_value); + + // Get and print solution variables + cuopt_float_t* solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + if (solution_values == NULL) { + printf("Error allocating solution values\n"); + goto DONE; + } + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + free(solution_values); + goto DONE; + } + + printf("\nPrimal Solution: Solution variables \n"); + for (cuopt_int_t i = 0; i < num_variables; i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + free(solution_values); + +DONE: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main() +{ + // Run the test + cuopt_int_t status = test_simple_lp(); + + if (status == CUOPT_SUCCESS) { + printf("\nTest completed successfully!\n"); + return 0; + } else { + printf("\nTest failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +## LP Example With MPS File + +Reads an LP or MPS file with `cuOptReadProblem` and solves it. The format is dispatched from the file extension (`.mps` / `.qps` → MPS parser; `.lp` → LP parser). + +{/* embed: examples/mps_file_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* + * MPS File C API Example (LP / QP / QCQP / SOCP) + * + * This example reads any problem from an MPS file and solves it with the cuOpt C + * API. The same program handles problems with + * - linear objectives (LP), + * - quadratic objectives (QP via a QUADOBJ section), and + * - quadratic constraints (QCQP/SOCP via QCMATRIX sections). + * + * + * Example LP problem (from sample.mps): + * Minimize: -0.2*VAR1 + 0.1*VAR2 + * Subject to: + * 3*VAR1 + 4*VAR2 <= 5.4 + * 2.7*VAR1 + 10.1*VAR2 <= 4.9 + * VAR1, VAR2 >= 0 + * + * Expected Output (sample.mps): + * Number of variables: 2 + * Termination status: Optimal (1) + * Solve time: 0.000014 seconds + * Objective value: -0.360000 + * x1 = 1.800000 + * x2 = 0.000000 + * + * See qp_sample.mps (quadratic objective) and socp_sample.mps (standard and + * rotated second-order cone constraints) for QP and SOCP inputs to this same + * program. + * + * Build: + * gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o mps_file_example mps_file_example.c -lcuopt + * + * Run: + * ./mps_file_example sample.mps + */ + +#include +#include +#include + +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: + return "Primal feasible"; + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: + return "Feasible found"; + default: + return "Unknown"; + } +} + +cuopt_int_t solve_mps_file(const char* filename) +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + cuopt_int_t num_variables; + cuopt_float_t* solution_values = NULL; + + printf("Reading and solving MPS file: %s\n", filename); + + // Create the problem from MPS file + status = cuOptReadProblem(filename, &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem from MPS file: %d\n", status); + goto DONE; + } + + // Get problem size + status = cuOptGetNumVariables(problem, &num_variables); + if (status != CUOPT_SUCCESS) { + printf("Error getting number of variables: %d\n", status); + goto DONE; + } + + // Create solver settings + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + // Set solver parameters + status = cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting optimality tolerance: %d\n", status); + goto DONE; + } + + // Solve the problem + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + // Get solution information + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + + // Print results + printf("\nResults:\n"); + printf("--------\n"); + printf("Number of variables: %d\n", num_variables); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + printf("Objective value: %f\n", objective_value); + + // Get and print solution variables + solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + goto DONE; + } + + printf("\nPrimal Solution: First 10 solution variables (or fewer if less exist):\n"); + for (cuopt_int_t i = 0; i < (num_variables < 10 ? num_variables : 10); i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + if (num_variables > 10) { + printf("... (showing only first 10 of %d variables)\n", num_variables); + } + +DONE: + free(solution_values); + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + // Run the solver + cuopt_int_t status = solve_mps_file(argv[1]); + + if (status == CUOPT_SUCCESS) { + printf("\nSolver completed successfully!\n"); + return 0; + } else { + printf("\nSolver failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +Sample MPS file used by this example: + +{/* embed: examples/sample.mps */} +```text +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 +``` +{/* /embed */} + +## LP Example With LP File + +Same `cuOptReadProblem` call with an LP-format file. + +{/* embed: examples/lp_file_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* + * LP File C API Example + * + * This example demonstrates how to solve an LP problem from an LP format + * file using the cuOpt C API. The same ``cuOptReadProblem`` call handles + * both MPS and LP inputs — the format is dispatched automatically by the + * file extension (case-insensitive): ``.lp`` / ``.lp.gz`` / ``.lp.bz2`` + * go to the LP parser; ``.mps`` / ``.qps`` and their ``.gz`` / ``.bz2`` + * variants go to the MPS parser. + * + * Problem (from LP file): + * Minimize: -0.2*VAR1 + 0.1*VAR2 + * Subject to: + * 3*VAR1 + 4*VAR2 <= 5.4 + * 2.7*VAR1 + 10.1*VAR2 <= 4.9 + * VAR1, VAR2 >= 0 + * + * Expected Output: + * Number of variables: 2 + * Termination status: Optimal (1) + * Objective value: -0.360000 + * x1 = 1.800000 + * x2 = 0.000000 + * + * Build: + * gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o lp_file_example lp_file_example.c -lcuopt + * + * Run: + * ./lp_file_example sample.lp + */ + +#include +#include +#include + +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: + return "Primal feasible"; + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: + return "Feasible found"; + default: + return "Unknown"; + } +} + +cuopt_int_t solve_lp_file(const char* filename) +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + cuopt_int_t num_variables; + cuopt_float_t* solution_values = NULL; + + printf("Reading and solving input file: %s\n", filename); + + // Create the problem from the input file. cuOptReadProblem dispatches on + // the file extension (case-insensitive): ``.lp`` / ``.lp.gz`` / ``.lp.bz2`` + // go to the LP parser; ``.mps`` / ``.qps`` and their ``.gz`` / ``.bz2`` + // variants go to the MPS parser. + status = cuOptReadProblem(filename, &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem from input file: %d\n", status); + goto DONE; + } + + status = cuOptGetNumVariables(problem, &num_variables); + if (status != CUOPT_SUCCESS) { + printf("Error getting number of variables: %d\n", status); + goto DONE; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + status = cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting optimality tolerance: %d\n", status); + goto DONE; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + + printf("\nResults:\n"); + printf("--------\n"); + printf("Number of variables: %d\n", num_variables); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + printf("Objective value: %f\n", objective_value); + + solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + goto DONE; + } + + printf("\nPrimal Solution: First 10 solution variables (or fewer if less exist):\n"); + for (cuopt_int_t i = 0; i < (num_variables < 10 ? num_variables : 10); i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + if (num_variables > 10) { + printf("... (showing only first 10 of %d variables)\n", num_variables); + } + +DONE: + free(solution_values); + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + cuopt_int_t status = solve_lp_file(argv[1]); + + if (status == CUOPT_SUCCESS) { + printf("\nSolver completed successfully!\n"); + return 0; + } else { + printf("\nSolver failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +Sample LP file (equivalent to the MPS sample above): + +{/* embed: examples/sample.lp */} +```text +\ Problem name: good-1 +\ Equivalent to sample.mps in this directory. + +Minimize + COST: - 0.2 VAR1 + 0.1 VAR2 +Subject To + ROW1: 3 VAR1 + 4 VAR2 <= 5.4 + ROW2: 2.7 VAR1 + 10.1 VAR2 <= 4.9 +End +``` +{/* /embed */} + +## Simple Quadratic Programming Example + +Builds and solves a small QP with `cuOptCreateProblem` and `cuOptSetQuadraticObjective`. + +{/* embed: examples/simple_qp_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights + * reserved. SPDX-License-Identifier: Apache-2.0 + */ +/* + * Simple QP C API Example + * + * This example demonstrates how to use the cuOpt C API for quadratic programming. + * + * Problem: + * Minimize: x^2 + y^2 + * Subject to: + * x + y >= 1 + * x, y >= 0 + * + * + * Build: + * gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o simple_qp_example simple_qp_example.c -lcuopt + * + * Run: + * ./simple_qp_example + */ + +// Include the cuOpt linear programming solver header +#include +#include +#include + +// Convert termination status to string +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: + return "Primal feasible"; + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: + return "Feasible found"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE: + return "Unbounded or infeasible"; + default: + return "Unknown"; + } +} + +// Test simple QP problem +cuopt_int_t test_simple_qp() +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + /* Solve the following QP: + minimize x^2 + y^2 + subject to: + x + y >= 1 + x, y >= 0 + */ + + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 1; + cuopt_int_t nnz = 2; + + // CSR format constraint matrix + // https://docs.nvidia.com/nvpl/latest/sparse/storage_format/sparse_matrix.html#compressed-sparse-row-csr + cuopt_int_t row_offsets[] = {0, 2}; + cuopt_int_t column_indices[] = {0, 1}; + cuopt_float_t values[] = {1.0, 1.0}; + + // Objective coefficients + // From the objective function: minimize x^2 + y^2 + // 0 is the coefficient of the linear term on x + // 0 is the coefficient of the linear term on y + cuopt_float_t linear_objective_coefficients[] = {0.0, 0.0}; + + // Quadratic objective matrix + // From the objective function: minimize x^2 + y^2 + // 1 is the coefficient of the quadratic term on x^2 + // 1 is the coefficient of the quadratic term on y^2 + cuopt_float_t quadratic_objective_matrix_values[] = {1.0, 1.0}; + cuopt_int_t quadratic_objective_matrix_row_offsets[] = {0, 1, 2}; + cuopt_int_t quadratic_objective_matrix_column_indices[] = {0, 1}; + + // Constraint bounds + // From the constraints: + // x + y >= 1 + cuopt_float_t constraint_rhs[] = {1.0}; + char constraint_sense[] = {CUOPT_GREATER_THAN}; + + // Variable bounds + // From the constraints: + // x1, x2 >= 0 + cuopt_float_t var_lower_bounds[] = {0.0, 0.0}; + cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + + // Variable types (continuous) + // From the constraints: + // x1, x2 >= 0 + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + + printf("Creating and solving simple QP problem...\n"); + + // Create the problem + status = cuOptCreateQuadraticProblem(num_constraints, + num_variables, + CUOPT_MINIMIZE, + 0.0, // objective offset + linear_objective_coefficients, + quadratic_objective_matrix_row_offsets, + quadratic_objective_matrix_column_indices, + quadratic_objective_matrix_values, + row_offsets, + column_indices, + values, + constraint_sense, + constraint_rhs, + var_lower_bounds, + var_upper_bounds, + &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + goto DONE; + } + + // Create solver settings + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + // Solve the problem + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + // Get solution information + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + + // Print results + printf("\nResults:\n"); + printf("--------\n"); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + printf("Objective value: %f\n", objective_value); + + // Get and print solution variables + cuopt_float_t* solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + if (solution_values == NULL) { + printf("Error allocating solution values\n"); + goto DONE; + } + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + free(solution_values); + goto DONE; + } + + printf("\nPrimal Solution: Solution variables \n"); + for (cuopt_int_t i = 0; i < num_variables; i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + free(solution_values); + +DONE: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main() +{ + // Run the test + cuopt_int_t status = test_simple_qp(); + + if (status == CUOPT_SUCCESS) { + printf("\nTest completed successfully!\n"); + return 0; + } else { + printf("\nTest failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +## Simple Second-Order Cone Programming Example + +Creates a problem with `cuOptCreateProblem`, then adds a second-order cone constraint with `cuOptAddQuadraticConstraint`. Minimizes `x3` subject to `x1 + x2 >= 2` and `||(x1, x2)||_2 <= x3`. The optimum is `x1 = x2 = 1`, `x3 = sqrt(2)`. + +{/* embed: examples/simple_socp_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights + * reserved. SPDX-License-Identifier: Apache-2.0 + */ +/* + * Simple SOCP C API Example + * + * Demonstrates building a second-order cone program (SOCP) with the cuOpt C API: + * a linear problem is created with cuOptCreateProblem, then a quadratic + * constraint is added with cuOptAddQuadraticConstraint. cuOpt detects the + * second-order cone structure of the quadratic constraint and solves with the + * barrier method. + * + * Problem: + * minimize x3 + * subject to x1 + x2 >= 2 + * x1^2 + x2^2 - x3^2 <= 0 (i.e. ||(x1, x2)||_2 <= x3) + * x3 >= 0, x1, x2 free + * + * Optimal: x1 = x2 = 1, x3 = sqrt(2) ~= 1.414214, objective ~= 1.414214. + * + * Build: + * gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o simple_socp_example simple_socp_example.c -lcuopt + * + * Run: + * ./simple_socp_example + */ + +#include +#include +#include + +// Convert termination status to string +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + default: + return "Unknown"; + } +} + +cuopt_int_t test_simple_socp() +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + cuopt_int_t num_variables = 3; // x1, x2, x3 + cuopt_int_t num_constraints = 1; // linear: x1 + x2 >= 2 (the cone is added separately) + + // Linear objective: minimize x3 + cuopt_float_t objective_coefficients[] = {0.0, 0.0, 1.0}; + + // Linear constraint matrix in CSR: row 0 is x1 + x2 + cuopt_int_t row_offsets[] = {0, 2}; + cuopt_int_t column_indices[] = {0, 1}; + cuopt_float_t values[] = {1.0, 1.0}; + char constraint_sense[] = {CUOPT_GREATER_THAN}; + cuopt_float_t rhs[] = {2.0}; + + // Variable bounds: x1, x2 free; x3 (the cone head) >= 0 + cuopt_float_t var_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY, 0.0}; + cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + // Second-order cone constraint x1^2 + x2^2 - x3^2 <= 0, supplied as the + // quadratic matrix Q in coordinate (triplet) form. There is no linear part + // and the right-hand side must be 0. + cuopt_int_t q_row_index[] = {0, 1, 2}; + cuopt_int_t q_col_index[] = {0, 1, 2}; + cuopt_float_t q_coeff[] = {1.0, 1.0, -1.0}; + + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + cuopt_float_t* solution_values = NULL; + + printf("Creating and solving simple SOCP problem...\n"); + + // Create the linear part of the problem + status = cuOptCreateProblem(num_constraints, + num_variables, + CUOPT_MINIMIZE, + 0.0, // objective offset + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_sense, + rhs, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + goto DONE; + } + + // Add the second-order cone constraint (no linear term, rhs = 0) + status = cuOptAddQuadraticConstraint(problem, + 3, // number of quadratic entries + q_row_index, + q_col_index, + q_coeff, + 0, // number of linear entries + NULL, // linear indices + NULL, // linear coefficients + CUOPT_LESS_THAN, + 0.0); + if (status != CUOPT_SUCCESS) { + printf("Error adding quadratic constraint: %d\n", status); + goto DONE; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + + printf("\nResults:\n"); + printf("--------\n"); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + printf("Objective value: %f\n", objective_value); + + solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + if (solution_values == NULL) { + printf("Error allocating solution values\n"); + goto DONE; + } + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + free(solution_values); + goto DONE; + } + + printf("\nPrimal Solution: Solution variables \n"); + for (cuopt_int_t i = 0; i < num_variables; i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + free(solution_values); + +DONE: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main() +{ + cuopt_int_t status = test_simple_socp(); + + if (status == CUOPT_SUCCESS) { + printf("\nTest completed successfully!\n"); + return 0; + } else { + printf("\nTest failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +## Rotated Second-Order Cone Example + +Adds a rotated cone `x1^2 + x2^2 <= x3 * x4` (written as `x1^2 + x2^2 - x3*x4 <= 0`). See [Convex Features](convex-features) for all supported RSOC forms. The optimum is `x1 = x2 = 1`, `x3 = x4 = sqrt(2)`. + +{/* embed: examples/rotated_socp_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights + * reserved. SPDX-License-Identifier: Apache-2.0 + */ +/* + * Rotated SOCP C API Example + * + * Demonstrates a rotated second-order cone with the cuOpt C API. + * + * Problem: + * minimize x3 + x4 + * subject to x1 + x2 >= 2 + * x1^2 + x2^2 <= x3*x4 + * x3 >= 0, x4 >= 0, x1, x2 free + * + * Optimal: x1 = x2 = 1, x3 = x4 = sqrt(2) ~= 1.414214, objective = 2*sqrt(2) ~= 2.828427. + * + * Build: + * gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o rotated_socp_example rotated_socp_example.c -lcuopt + * + * Run: + * ./rotated_socp_example + */ + +#include +#include +#include + +// Convert termination status to string +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + default: + return "Unknown"; + } +} + +cuopt_int_t test_rotated_socp() +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + cuopt_int_t num_variables = 4; // x1, x2, x3, x4 + cuopt_int_t num_constraints = 1; // linear: x1 + x2 >= 2 (the cone is added separately) + + // Linear objective: minimize x3 + x4 + cuopt_float_t objective_coefficients[] = {0.0, 0.0, 1.0, 1.0}; + + // Linear constraint matrix in CSR: row 0 is x1 + x2 + cuopt_int_t row_offsets[] = {0, 2}; + cuopt_int_t column_indices[] = {0, 1}; + cuopt_float_t values[] = {1.0, 1.0}; + char constraint_sense[] = {CUOPT_GREATER_THAN}; + cuopt_float_t rhs[] = {2.0}; + + // Variable bounds: x1, x2 free; x3, x4 (the cone heads) >= 0 + cuopt_float_t var_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY, 0.0, 0.0}; + cuopt_float_t var_upper_bounds[] = { + CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = { + CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + // Rotated cone x1^2 + x2^2 <= x3*x4. + cuopt_int_t q_row_index[] = {0, 1, 2}; + cuopt_int_t q_col_index[] = {0, 1, 3}; + cuopt_float_t q_coeff[] = {1.0, 1.0, -1.0}; + + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + cuopt_float_t* solution_values = NULL; + + printf("Creating and solving rotated SOCP problem...\n"); + + // Create the linear part of the problem + status = cuOptCreateProblem(num_constraints, + num_variables, + CUOPT_MINIMIZE, + 0.0, // objective offset + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_sense, + rhs, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + goto DONE; + } + + // Add the rotated second-order cone constraint (no linear term, rhs = 0) + status = cuOptAddQuadraticConstraint(problem, + 3, // number of quadratic entries + q_row_index, + q_col_index, + q_coeff, + 0, // number of linear entries + NULL, // linear indices + NULL, // linear coefficients + CUOPT_LESS_THAN, + 0.0); + if (status != CUOPT_SUCCESS) { + printf("Error adding quadratic constraint: %d\n", status); + goto DONE; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + + printf("\nResults:\n"); + printf("--------\n"); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + printf("Objective value: %f\n", objective_value); + + solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + if (solution_values == NULL) { + printf("Error allocating solution values\n"); + goto DONE; + } + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + free(solution_values); + goto DONE; + } + + printf("\nPrimal Solution: Solution variables \n"); + for (cuopt_int_t i = 0; i < num_variables; i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + free(solution_values); + +DONE: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main() +{ + cuopt_int_t status = test_rotated_socp(); + + if (status == CUOPT_SUCCESS) { + printf("\nTest completed successfully!\n"); + return 0; + } else { + printf("\nTest failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +## General Convex Quadratic Constraint Example + +Adds the convex constraint `2*x^2 + 2*x*y + 2*y^2 <= 6` with a non-symmetric Q matrix (the cross-term `2*x*y` is supplied as a single entry). The optimum is `x = y = -1`. + +{/* embed: examples/general_quadratic_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights + * reserved. SPDX-License-Identifier: Apache-2.0 + */ +/* + * General Convex Quadratic Constraint C API Example + * + * Demonstrates a general convex quadratic constraint x^T Q x + d^T x <= alpha + * with the cuOpt C API. A linear problem is created with cuOptCreateProblem, + * then the quadratic constraint is added with cuOptAddQuadraticConstraint. + * + * Q may be any matrix whose symmetric part (Q + Q^T)/2 is positive semidefinite. + * Q need not be supplied symmetrically: here the cross term 2*x*y is given as + * one canonical COO entry Q[0,1] = 2 (full coefficient on x*y, canonicalized + * on ingest). + * + * Problem: + * minimize x + y + * subject to x + y >= -5 + * 2*x^2 + 2*x*y + 2*y^2 <= 6 (general convex quadratic) + * + * Optimal: x = y = -1, objective = -2. + * + * Build: + * gcc -I $INCLUDE_PATH -L $LIBCUOPT_LIBRARY_PATH -o general_quadratic_example general_quadratic_example.c -lcuopt + * + * Run: + * ./general_quadratic_example + */ + +#include +#include +#include + +// Convert termination status to string +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + default: + return "Unknown"; + } +} + +cuopt_int_t test_general_quadratic() +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + cuopt_int_t num_variables = 2; // x, y + cuopt_int_t num_constraints = 1; // linear: x + y >= -5 (the quadratic is added separately) + + // Linear objective: minimize x + y + cuopt_float_t objective_coefficients[] = {1.0, 1.0}; + + // Linear constraint matrix in CSR: row 0 is x + y + cuopt_int_t row_offsets[] = {0, 2}; + cuopt_int_t column_indices[] = {0, 1}; + cuopt_float_t values[] = {1.0, 1.0}; + char constraint_sense[] = {CUOPT_GREATER_THAN}; + cuopt_float_t rhs[] = {-5.0}; + + // Variables are free + cuopt_float_t var_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY}; + cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + // General convex quadratic constraint 2*x^2 + 2*x*y + 2*y^2 <= 6 in canonical COO form. + cuopt_int_t q_row_index[] = {0, 1, 0}; + cuopt_int_t q_col_index[] = {0, 1, 1}; + cuopt_float_t q_coeff[] = {2.0, 2.0, 2.0}; + + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + cuopt_float_t* solution_values = NULL; + + printf("Creating and solving general convex quadratic problem...\n"); + + // Create the linear part of the problem + status = cuOptCreateProblem(num_constraints, + num_variables, + CUOPT_MINIMIZE, + 0.0, // objective offset + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_sense, + rhs, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + goto DONE; + } + + // Add the general convex quadratic constraint (no linear term, rhs = 6) + status = cuOptAddQuadraticConstraint(problem, + 3, // number of quadratic entries + q_row_index, + q_col_index, + q_coeff, + 0, // number of linear entries + NULL, // linear indices + NULL, // linear coefficients + CUOPT_LESS_THAN, + 6.0); + if (status != CUOPT_SUCCESS) { + printf("Error adding quadratic constraint: %d\n", status); + goto DONE; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + + printf("\nResults:\n"); + printf("--------\n"); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + printf("Objective value: %f\n", objective_value); + + solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + if (solution_values == NULL) { + printf("Error allocating solution values\n"); + goto DONE; + } + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + free(solution_values); + goto DONE; + } + + printf("\nPrimal Solution: Solution variables \n"); + for (cuopt_int_t i = 0; i < num_variables; i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + free(solution_values); + +DONE: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main() +{ + cuopt_int_t status = test_general_quadratic(); + + if (status == CUOPT_SUCCESS) { + printf("\nTest completed successfully!\n"); + return 0; + } else { + printf("\nTest failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +## QP Example With MPS File + +The same `mps_file_example.c` binary also solves QP problems. A quadratic objective is provided via a `QUADOBJ` section in the MPS file. + +Sample QP file (minimizes `x^2 + y^2` subject to `x + y >= 1`; optimum `x = y = 0.5`, objective `0.5`): + +{/* embed: examples/qp_sample.mps */} +```text +NAME QPSAMPLE +ROWS + N OBJ + G C1 +COLUMNS + X C1 1.0 + Y C1 1.0 +RHS + RHS C1 1.0 +QUADOBJ + X X 2.0 + Y Y 2.0 +ENDATA +``` +{/* /embed */} + +## SOCP Example With MPS File + +SOCP constraints are expressed through `QCMATRIX` sections. Both standard and rotated cones are supported; see [LP/QP/QCQP/SOCP Features](convex-features) for the supported cone forms. + +Sample SOCP file (minimizes `s + p + q` subject to two cones; optimum `s = p = q = sqrt(2)`): + +{/* embed: examples/socp_sample.mps */} +```text +NAME SOCPSAMPLE +ROWS + N OBJ + G C1 + L QCSTD + L QCROT +COLUMNS + A C1 1.0 + B C1 1.0 + S OBJ 1.0 + P OBJ 1.0 + Q OBJ 1.0 +RHS + RHS C1 2.0 +BOUNDS + FR BND A + FR BND B +QCMATRIX QCSTD + A A 1.0 + B B 1.0 + S S -1.0 +QCMATRIX QCROT + A A 1.0 + B B 1.0 + P Q -0.5 + Q P -0.5 +ENDATA +``` +{/* /embed */} diff --git a/docs/cuopt/source/cuopt-c/convex/examples/Makefile b/fern/docs/pages/cuopt-c/convex/examples/Makefile similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/Makefile rename to fern/docs/pages/cuopt-c/convex/examples/Makefile diff --git a/docs/cuopt/source/cuopt-c/convex/examples/general_quadratic_example.c b/fern/docs/pages/cuopt-c/convex/examples/general_quadratic_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/general_quadratic_example.c rename to fern/docs/pages/cuopt-c/convex/examples/general_quadratic_example.c diff --git a/docs/cuopt/source/cuopt-c/convex/examples/lp_file_example.c b/fern/docs/pages/cuopt-c/convex/examples/lp_file_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/lp_file_example.c rename to fern/docs/pages/cuopt-c/convex/examples/lp_file_example.c diff --git a/docs/cuopt/source/cuopt-c/convex/examples/mps_file_example.c b/fern/docs/pages/cuopt-c/convex/examples/mps_file_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/mps_file_example.c rename to fern/docs/pages/cuopt-c/convex/examples/mps_file_example.c diff --git a/docs/cuopt/source/cuopt-c/convex/examples/qp_sample.mps b/fern/docs/pages/cuopt-c/convex/examples/qp_sample.mps similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/qp_sample.mps rename to fern/docs/pages/cuopt-c/convex/examples/qp_sample.mps diff --git a/docs/cuopt/source/cuopt-c/convex/examples/rotated_socp_example.c b/fern/docs/pages/cuopt-c/convex/examples/rotated_socp_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/rotated_socp_example.c rename to fern/docs/pages/cuopt-c/convex/examples/rotated_socp_example.c diff --git a/docs/cuopt/source/cuopt-c/convex/examples/sample.lp b/fern/docs/pages/cuopt-c/convex/examples/sample.lp similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/sample.lp rename to fern/docs/pages/cuopt-c/convex/examples/sample.lp diff --git a/docs/cuopt/source/cuopt-c/convex/examples/sample.mps b/fern/docs/pages/cuopt-c/convex/examples/sample.mps similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/sample.mps rename to fern/docs/pages/cuopt-c/convex/examples/sample.mps diff --git a/docs/cuopt/source/cuopt-c/convex/examples/simple_lp_example.c b/fern/docs/pages/cuopt-c/convex/examples/simple_lp_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/simple_lp_example.c rename to fern/docs/pages/cuopt-c/convex/examples/simple_lp_example.c diff --git a/docs/cuopt/source/cuopt-c/convex/examples/simple_qp_example.c b/fern/docs/pages/cuopt-c/convex/examples/simple_qp_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/simple_qp_example.c rename to fern/docs/pages/cuopt-c/convex/examples/simple_qp_example.c diff --git a/docs/cuopt/source/cuopt-c/convex/examples/simple_socp_example.c b/fern/docs/pages/cuopt-c/convex/examples/simple_socp_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/simple_socp_example.c rename to fern/docs/pages/cuopt-c/convex/examples/simple_socp_example.c diff --git a/docs/cuopt/source/cuopt-c/convex/examples/socp_sample.mps b/fern/docs/pages/cuopt-c/convex/examples/socp_sample.mps similarity index 100% rename from docs/cuopt/source/cuopt-c/convex/examples/socp_sample.mps rename to fern/docs/pages/cuopt-c/convex/examples/socp_sample.mps diff --git a/fern/docs/pages/cuopt-c/convex/index.mdx b/fern/docs/pages/cuopt-c/convex/index.mdx new file mode 100644 index 0000000000..d42198cb87 --- /dev/null +++ b/fern/docs/pages/cuopt-c/convex/index.mdx @@ -0,0 +1,5 @@ +--- +title: "Convex Optimization (LP/QP/QCQP/SOCP)" +--- + +This section contains details on the cuOpt convex optimization C API. diff --git a/fern/docs/pages/cuopt-c/index.mdx b/fern/docs/pages/cuopt-c/index.mdx new file mode 100644 index 0000000000..0456f00c33 --- /dev/null +++ b/fern/docs/pages/cuopt-c/index.mdx @@ -0,0 +1,5 @@ +--- +title: "C API" +--- + +NVIDIA cuOpt supports a C API for GPU-accelerated optimization that enables users to solve complex optimization problems efficiently. The cuOpt C API supports convex optimization (LP, QP, QCQP, and SOCP) and mixed-integer programming (MIP). diff --git a/docs/cuopt/source/cuopt-c/mip/examples/Makefile b/fern/docs/pages/cuopt-c/mip/examples/Makefile similarity index 100% rename from docs/cuopt/source/cuopt-c/mip/examples/Makefile rename to fern/docs/pages/cuopt-c/mip/examples/Makefile diff --git a/docs/cuopt/source/cuopt-c/mip/examples/milp_mps_example.c b/fern/docs/pages/cuopt-c/mip/examples/milp_mps_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/mip/examples/milp_mps_example.c rename to fern/docs/pages/cuopt-c/mip/examples/milp_mps_example.c diff --git a/docs/cuopt/source/cuopt-c/mip/examples/mip_sample.mps b/fern/docs/pages/cuopt-c/mip/examples/mip_sample.mps similarity index 100% rename from docs/cuopt/source/cuopt-c/mip/examples/mip_sample.mps rename to fern/docs/pages/cuopt-c/mip/examples/mip_sample.mps diff --git a/docs/cuopt/source/cuopt-c/mip/examples/simple_milp_example.c b/fern/docs/pages/cuopt-c/mip/examples/simple_milp_example.c similarity index 100% rename from docs/cuopt/source/cuopt-c/mip/examples/simple_milp_example.c rename to fern/docs/pages/cuopt-c/mip/examples/simple_milp_example.c diff --git a/fern/docs/pages/cuopt-c/mip/index.mdx b/fern/docs/pages/cuopt-c/mip/index.mdx new file mode 100644 index 0000000000..615f66c6b0 --- /dev/null +++ b/fern/docs/pages/cuopt-c/mip/index.mdx @@ -0,0 +1,9 @@ +--- +title: "MIP (Mixed Integer Programming)" +--- + + +The cuOpt MIP solver is in **beta** and under active development. The solver currently excels at finding high-quality feasible solutions quickly with GPU-accelerated primal heuristics. Proving feasible solutions optimal remains under active development. + + +This section contains details on the cuOpt MIP C API. diff --git a/fern/docs/pages/cuopt-c/mip/mip-c-api.mdx b/fern/docs/pages/cuopt-c/mip/mip-c-api.mdx new file mode 100644 index 0000000000..9c8757a38b --- /dev/null +++ b/fern/docs/pages/cuopt-c/mip/mip-c-api.mdx @@ -0,0 +1,430 @@ +--- +title: "cuOpt MIP C API Reference" +--- + +This section contains the cuOpt MIP C API reference. Functions for problem creation, +solver settings, solving, and inspecting solutions are shared with convex optimization +and documented in [Convex C API](../convex/convex-c-api). + +## Warm Start and MIP Start + +For LP problems solved with PDLP, see [Convex C API](../convex/convex-c-api) for primal and dual warm start. +For MIP problems, one or more primal solution hints (MIP starts) may be provided. + +{/* symbol: cuOptAddMIPStart */} +#### `cuOptAddMIPStart(cuOptSolverSettings settings, const cuopt_float_t * solution, cuopt_int_t num_variables) -> cuopt_int_t` + +Add an initial solution (MIP start) for MIP solving. + + +Currently unsupported with presolve on. All pointer arguments (solution) refer to host memory. + + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`solution`** (`const cuopt_float_t *`) `[in]` — A pointer to an array of type cuopt_float_t of size num_variables containing the solution values. +- **`num_variables`** (`cuopt_int_t`) `[in]` — The number of variables (size of the solution array). + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +## MIP Solution Callbacks + +The following callback types and functions allow monitoring and injecting solutions during a MIP solve. + +{/* symbol: cuOptMIPGetSolutionCallback */} +**`typedef`** **`cuOptMIPGetSolutionCallback`** — Type of callback for receiving incumbent MIP solutions with user context. + + +All pointer arguments (solution, objective_value, solution_bound, user_data) refer to host memory and are only valid during the callback invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback returns. + + +**Parameters** + +- **`solution`** `[in]` — Pointer to incumbent solution values. The allocated array for solution pointer must be at least the number of variables in the original problem. +- **`objective_value`** `[in]` — Pointer to incumbent objective value. +- **`solution_bound`** `[in]` — Pointer to current solution (dual/user) bound. +- **`user_data`** `[in]` — Pointer to user data. +{/* /symbol */} + +{/* symbol: cuOptMIPSetSolutionCallback */} +**`typedef`** **`cuOptMIPSetSolutionCallback`** — Type of callback for injecting MIP solutions with user context. + + +All pointer arguments (solution, objective_value, solution_bound, user_data) refer to host memory and are only valid during the callback invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback returns. + + +**Parameters** + +- **`solution`** `[out]` — Pointer to solution values to set. The allocated array for solution pointer must be at least the number of variables in the original problem. +- **`objective_value`** `[out]` — Pointer to objective value to set. +- **`solution_bound`** `[in]` — Pointer to current solution (dual/user) bound. +- **`user_data`** `[in]` — Pointer to user data. +{/* /symbol */} + +{/* symbol: cuOptSetMIPGetSolutionCallback */} +#### `cuOptSetMIPGetSolutionCallback(cuOptSolverSettings settings, cuOptMIPGetSolutionCallback callback, void * user_data) -> cuopt_int_t` + +Register a callback to receive incumbent MIP solutions. + + +The callback arguments refer to host memory and are only valid during the callback invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback returns. + + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`callback`** (`cuOptMIPGetSolutionCallback`) `[in]` — Callback function to receive incumbent solutions. +- **`user_data`** (`void *`) `[in]` — User-defined pointer passed through to the callback. It will be forwarded to `cuOptMIPGetSolutionCallback` when invoked. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +{/* symbol: cuOptSetMIPSetSolutionCallback */} +#### `cuOptSetMIPSetSolutionCallback(cuOptSolverSettings settings, cuOptMIPSetSolutionCallback callback, void * user_data) -> cuopt_int_t` + +Register a callback to inject MIP solutions. + + +Registering a set-solution callback disables presolve. The callback arguments refer to host memory and are only valid during the callback invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback returns. + + +**Parameters** + +- **`settings`** (`cuOptSolverSettings`) `[in]` — The solver settings object. +- **`callback`** (`cuOptMIPSetSolutionCallback`) `[in]` — Callback function to inject solutions. +- **`user_data`** (`void *`) `[in]` — User-defined pointer passed through to the callback. It will be forwarded to `cuOptMIPSetSolutionCallback` when invoked. + +**Returns** A status code indicating success or failure. +{/* /symbol */} + +## MIP Parameter Constants + +These constants configure MIP-specific solver behavior. Use them with +`cuOptSetParameter`, `cuOptSetIntegerParameter`, or `cuOptSetFloatParameter`. +For shared parameters (time limit, logging, presolve, etc.), see +[Parameter Constants](../convex/convex-c-api#parameter-constants) in the convex API reference. + +{/* symbol: CUOPT_NODE_LIMIT */} +- `CUOPT_NODE_LIMIT` (`"node_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_WORK_LIMIT */} +- `CUOPT_WORK_LIMIT` (`"work_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_RANDOM_SEED */} +- `CUOPT_RANDOM_SEED` (`"random_seed"`) +{/* /symbol */} + +{/* symbol: CUOPT_PRESOLVE_FILE */} +- `CUOPT_PRESOLVE_FILE` (`"presolve_file"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_ABSOLUTE_TOLERANCE */} +- `CUOPT_MIP_ABSOLUTE_TOLERANCE` (`"mip_absolute_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_RELATIVE_TOLERANCE */} +- `CUOPT_MIP_RELATIVE_TOLERANCE` (`"mip_relative_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_INTEGRALITY_TOLERANCE */} +- `CUOPT_MIP_INTEGRALITY_TOLERANCE` (`"mip_integrality_tolerance"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_ABSOLUTE_GAP */} +- `CUOPT_MIP_ABSOLUTE_GAP` (`"mip_absolute_gap"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_RELATIVE_GAP */} +- `CUOPT_MIP_RELATIVE_GAP` (`"mip_relative_gap"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_SCALING */} +- `CUOPT_MIP_SCALING` (`"mip_scaling"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HEURISTICS_ONLY */} +- `CUOPT_MIP_HEURISTICS_ONLY` (`"mip_heuristics_only"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_PRESOLVE */} +- `CUOPT_MIP_PRESOLVE` (`"mip_presolve"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_DETERMINISM_MODE */} +- `CUOPT_MIP_DETERMINISM_MODE` (`"mip_determinism_mode"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_SYMMETRY */} +- `CUOPT_MIP_SYMMETRY` (`"mip_symmetry"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_PROBING */} +- `CUOPT_MIP_PROBING` (`"mip_probing"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_RELIABILITY_BRANCHING */} +- `CUOPT_MIP_RELIABILITY_BRANCHING` (`"mip_reliability_branching"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_CUT_PASSES */} +- `CUOPT_MIP_CUT_PASSES` (`"mip_cut_passes"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS */} +- `CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS` (`"mip_mixed_integer_rounding_cuts"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS */} +- `CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS` (`"mip_mixed_integer_gomory_cuts"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_KNAPSACK_CUTS */} +- `CUOPT_MIP_KNAPSACK_CUTS` (`"mip_knapsack_cuts"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_FLOW_COVER_CUTS */} +- `CUOPT_MIP_FLOW_COVER_CUTS` (`"mip_flow_cover_cuts"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_IMPLIED_BOUND_CUTS */} +- `CUOPT_MIP_IMPLIED_BOUND_CUTS` (`"mip_implied_bound_cuts"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_CLIQUE_CUTS */} +- `CUOPT_MIP_CLIQUE_CUTS` (`"mip_clique_cuts"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_ZERO_HALF_CUTS */} +- `CUOPT_MIP_ZERO_HALF_CUTS` (`"mip_zero_half_cuts"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS */} +- `CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS` (`"mip_strong_chvatal_gomory_cuts"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_REDUCED_COST_STRENGTHENING */} +- `CUOPT_MIP_REDUCED_COST_STRENGTHENING` (`"mip_reduced_cost_strengthening"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_RINS */} +- `CUOPT_MIP_RINS` (`"mip_rins"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_OBJECTIVE_STEP */} +- `CUOPT_MIP_OBJECTIVE_STEP` (`"mip_objective_step"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_CUT_CHANGE_THRESHOLD */} +- `CUOPT_MIP_CUT_CHANGE_THRESHOLD` (`"mip_cut_change_threshold"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_CUT_MIN_ORTHOGONALITY */} +- `CUOPT_MIP_CUT_MIN_ORTHOGONALITY` (`"mip_cut_min_orthogonality"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING */} +- `CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING` (`"mip_batch_pdlp_strong_branching"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_BATCH_PDLP_RELIABILITY_BRANCHING */} +- `CUOPT_MIP_BATCH_PDLP_RELIABILITY_BRANCHING` (`"mip_batch_pdlp_reliability_branching"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITERATION_LIMIT */} +- `CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITERATION_LIMIT` (`"mip_strong_branching_simplex_iteration_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_SEMICONTINUOUS_BIG_M */} +- `CUOPT_MIP_SEMICONTINUOUS_BIG_M` (`"mip_semi_continuous_big_m"`) +{/* /symbol */} + +## MIP Scaling Constants + +These constants are used to configure `CUOPT_MIP_SCALING` via `cuOptSetIntegerParameter`. + +{/* symbol: CUOPT_MIP_SCALING_OFF */} +- `CUOPT_MIP_SCALING_OFF` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_SCALING_ON */} +- `CUOPT_MIP_SCALING_ON` (`1`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_SCALING_NO_OBJECTIVE */} +- `CUOPT_MIP_SCALING_NO_OBJECTIVE` (`2`) +{/* /symbol */} + +## MIP Determinism Mode Constants + +These constants are used to configure `CUOPT_MIP_DETERMINISM_MODE` via `cuOptSetIntegerParameter`. + +{/* symbol: CUOPT_MODE_OPPORTUNISTIC */} +- `CUOPT_MODE_OPPORTUNISTIC` (`0`) +{/* /symbol */} + +{/* symbol: CUOPT_MODE_DETERMINISTIC */} +- `CUOPT_MODE_DETERMINISTIC` (`1`) +{/* /symbol */} + +## MIP Hyper Diving Parameters + +These constants configure the hyper diving heuristic. Use them with +`cuOptSetParameter`, `cuOptSetIntegerParameter`, or `cuOptSetFloatParameter`. + +{/* symbol: CUOPT_MIP_HYPER_DIVING_BACKTRACK_LIMIT */} +- `CUOPT_MIP_HYPER_DIVING_BACKTRACK_LIMIT` (`"mip_hyper_diving_backtrack_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_COEFFICIENT */} +- `CUOPT_MIP_HYPER_DIVING_COEFFICIENT` (`"mip_hyper_diving_coefficient"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_FARKAS */} +- `CUOPT_MIP_HYPER_DIVING_FARKAS` (`"mip_hyper_diving_farkas"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_GUIDED */} +- `CUOPT_MIP_HYPER_DIVING_GUIDED` (`"mip_hyper_diving_guided"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_ITERATION_LIMIT_FACTOR */} +- `CUOPT_MIP_HYPER_DIVING_ITERATION_LIMIT_FACTOR` (`"mip_hyper_diving_iteration_limit_factor"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_LINE_SEARCH */} +- `CUOPT_MIP_HYPER_DIVING_LINE_SEARCH` (`"mip_hyper_diving_line_search"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_MIN_NODE_DEPTH */} +- `CUOPT_MIP_HYPER_DIVING_MIN_NODE_DEPTH` (`"mip_hyper_diving_min_node_depth"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_NODE_LIMIT */} +- `CUOPT_MIP_HYPER_DIVING_NODE_LIMIT` (`"mip_hyper_diving_node_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_PSEUDOCOST */} +- `CUOPT_MIP_HYPER_DIVING_PSEUDOCOST` (`"mip_hyper_diving_pseudocost"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_SHOW_TYPE */} +- `CUOPT_MIP_HYPER_DIVING_SHOW_TYPE` (`"mip_hyper_diving_show_type"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_DIVING_VECTOR_LENGTH */} +- `CUOPT_MIP_HYPER_DIVING_VECTOR_LENGTH` (`"mip_hyper_diving_vector_length"`) +{/* /symbol */} + +## MIP Hyper Heuristic Parameters + +These constants configure the hyper heuristic solver. Use them with +`cuOptSetParameter`, `cuOptSetIntegerParameter`, or `cuOptSetFloatParameter`. + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_CYCLE_DETECTION_LENGTH */} +- `CUOPT_MIP_HYPER_HEURISTIC_CYCLE_DETECTION_LENGTH` (`"mip_hyper_heuristic_cycle_detection_length"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_ENABLED_RECOMBINERS */} +- `CUOPT_MIP_HYPER_HEURISTIC_ENABLED_RECOMBINERS` (`"mip_hyper_heuristic_enabled_recombiners"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_INITIAL_INFEASIBILITY_WEIGHT */} +- `CUOPT_MIP_HYPER_HEURISTIC_INITIAL_INFEASIBILITY_WEIGHT` (`"mip_hyper_heuristic_initial_infeasibility_weight"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_MAX_ITERS_WITHOUT_IMPROVEMENT */} +- `CUOPT_MIP_HYPER_HEURISTIC_MAX_ITERS_WITHOUT_IMPROVEMENT` (`"mip_hyper_heuristic_max_iterations_without_improvement"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_NUM_CPUFJ_THREADS */} +- `CUOPT_MIP_HYPER_HEURISTIC_NUM_CPUFJ_THREADS` (`"mip_hyper_heuristic_num_cpufj_threads"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_N_OF_MINIMUMS_FOR_EXIT */} +- `CUOPT_MIP_HYPER_HEURISTIC_N_OF_MINIMUMS_FOR_EXIT` (`"mip_hyper_heuristic_n_of_minimums_for_exit"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_POPULATION_SIZE */} +- `CUOPT_MIP_HYPER_HEURISTIC_POPULATION_SIZE` (`"mip_hyper_heuristic_population_size"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_PRESOLVE_MAX_TIME */} +- `CUOPT_MIP_HYPER_HEURISTIC_PRESOLVE_MAX_TIME` (`"mip_hyper_heuristic_presolve_max_time"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_PRESOLVE_TIME_RATIO */} +- `CUOPT_MIP_HYPER_HEURISTIC_PRESOLVE_TIME_RATIO` (`"mip_hyper_heuristic_presolve_time_ratio"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_RELATED_VARS_TIME_LIMIT */} +- `CUOPT_MIP_HYPER_HEURISTIC_RELATED_VARS_TIME_LIMIT` (`"mip_hyper_heuristic_related_vars_time_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_RELAXED_LP_TIME_LIMIT */} +- `CUOPT_MIP_HYPER_HEURISTIC_RELAXED_LP_TIME_LIMIT` (`"mip_hyper_heuristic_relaxed_lp_time_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_RINS_FIX_RATE */} +- `CUOPT_MIP_HYPER_HEURISTIC_RINS_FIX_RATE` (`"mip_hyper_heuristic_rins_fix_rate"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_RINS_MAX_TIME_LIMIT */} +- `CUOPT_MIP_HYPER_HEURISTIC_RINS_MAX_TIME_LIMIT` (`"mip_hyper_heuristic_rins_max_time_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_RINS_TIME_LIMIT */} +- `CUOPT_MIP_HYPER_HEURISTIC_RINS_TIME_LIMIT` (`"mip_hyper_heuristic_rins_time_limit"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_ROOT_LP_MAX_TIME */} +- `CUOPT_MIP_HYPER_HEURISTIC_ROOT_LP_MAX_TIME` (`"mip_hyper_heuristic_root_lp_max_time"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_ROOT_LP_TIME_RATIO */} +- `CUOPT_MIP_HYPER_HEURISTIC_ROOT_LP_TIME_RATIO` (`"mip_hyper_heuristic_root_lp_time_ratio"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_HEURISTIC_STAGNATION_TRIGGER */} +- `CUOPT_MIP_HYPER_HEURISTIC_STAGNATION_TRIGGER` (`"mip_hyper_heuristic_stagnation_trigger"`) +{/* /symbol */} + +## MIP Sub-MIP Hyper Parameters + +These constants configure the recursive sub-MIP (RINS) heuristic. Use them with +`cuOptSetParameter`, `cuOptSetIntegerParameter`, or `cuOptSetFloatParameter`. + +{/* symbol: CUOPT_MIP_HYPER_SUBMIP_BASE_TARGET_FIXRATE */} +- `CUOPT_MIP_HYPER_SUBMIP_BASE_TARGET_FIXRATE` (`"mip_hyper_submip_base_target_fixrate"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_SUBMIP_ENABLE_CPUFJ */} +- `CUOPT_MIP_HYPER_SUBMIP_ENABLE_CPUFJ` (`"mip_hyper_submip_enable_cpufj"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_SUBMIP_ITERATION_LIMIT_RATIO */} +- `CUOPT_MIP_HYPER_SUBMIP_ITERATION_LIMIT_RATIO` (`"mip_hyper_submip_iteration_limit_ratio"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_SUBMIP_MAX_LEVEL */} +- `CUOPT_MIP_HYPER_SUBMIP_MAX_LEVEL` (`"mip_hyper_submip_max_level"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_SUBMIP_MIN_FIXRATE */} +- `CUOPT_MIP_HYPER_SUBMIP_MIN_FIXRATE` (`"mip_hyper_submip_min_fixrate"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_SUBMIP_MIN_FIXRATE_CAP */} +- `CUOPT_MIP_HYPER_SUBMIP_MIN_FIXRATE_CAP` (`"mip_hyper_submip_min_fixrate_cap"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_SUBMIP_NODE_LIMIT_BASE */} +- `CUOPT_MIP_HYPER_SUBMIP_NODE_LIMIT_BASE` (`"mip_hyper_submip_node_limit_base"`) +{/* /symbol */} + +{/* symbol: CUOPT_MIP_HYPER_SUBMIP_TARGET_MIP_GAP */} +- `CUOPT_MIP_HYPER_SUBMIP_TARGET_MIP_GAP` (`"mip_hyper_submip_target_mip_gap"`) +{/* /symbol */} diff --git a/fern/docs/pages/cuopt-c/mip/mip-examples.mdx b/fern/docs/pages/cuopt-c/mip/mip-examples.mdx new file mode 100644 index 0000000000..33f0e22397 --- /dev/null +++ b/fern/docs/pages/cuopt-c/mip/mip-examples.mdx @@ -0,0 +1,447 @@ +--- +title: "MILP C API Examples" +--- + +More details on the API can be found in [MIP C API](mip-c-api). + +**Build prerequisites:** Set `CUOPT_INCLUDE` and `CUOPT_LIB` to your cuOpt include and library paths, then use the provided `Makefile` in the examples directory: + +```bash +make INCLUDE_PATH=$CUOPT_INCLUDE LIBCUOPT_LIBRARY_PATH=$CUOPT_LIB all +``` + +If you built cuOpt locally, `CUOPT_INCLUDE=cpp/include` and `CUOPT_LIB=cpp/build`. + +## MILP Example With Data + +Builds and solves a small MILP directly with the C API using `cuOptCreateProblem` with integer variable types. + +{/* embed: examples/simple_milp_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* + * Simple test program for cuOpt MILP solver + */ + +// Include the cuOpt linear programming solver header +#include +#include +#include + +// Convert termination status to string +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: + return "Primal feasible"; + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: + return "Feasible found"; + default: + return "Unknown"; + } +} + +// Test simple MILP problem +cuopt_int_t test_simple_milp() +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + /* Solve the following MILP: + minimize -0.2*x1 + 0.1*x2 + subject to: + 3.0*x1 + 4.0*x2 <= 5.4 + 2.7*x1 + 10.1*x2 <= 4.9 + x1, x2 >= 0 + x1 is integer + x2 is continuous + */ + + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 2; + cuopt_int_t nnz = 4; + + // CSR format constraint matrix + // https://docs.nvidia.com/nvpl/latest/sparse/storage_format/sparse_matrix.html#compressed-sparse-row-csr + // From the constraints: + // 3.0*x1 + 4.0*x2 <= 5.4 + // 2.7*x1 + 10.1*x2 <= 4.9 + cuopt_int_t row_offsets[] = {0, 2, 4}; + cuopt_int_t column_indices[] = {0, 1, 0, 1}; + cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; + + // Objective coefficients + // From the objective function: minimize -0.2*x1 + 0.1*x2 + // -0.2 is the coefficient of x1 + // 0.1 is the coefficient of x2 + cuopt_float_t objective_coefficients[] = {-0.2, 0.1}; + + // Constraint bounds + // From the constraints: + // 3.0*x1 + 4.0*x2 <= 5.4 + // 2.7*x1 + 10.1*x2 <= 4.9 + cuopt_float_t constraint_upper_bounds[] = {5.4, 4.9}; + cuopt_float_t constraint_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY}; + + // Variable bounds + // From the constraints: + // x1, x2 >= 0 + cuopt_float_t var_lower_bounds[] = {0.0, 0.0}; + cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + + // Variable types + // x1 is integer + // x2 is continuous + char variable_types[] = {CUOPT_INTEGER, CUOPT_CONTINUOUS}; + + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + + printf("Creating and solving simple MILP problem...\n"); + + // Create the problem + status = cuOptCreateRangedProblem(num_constraints, + num_variables, + CUOPT_MINIMIZE, + 0.0, // objective offset + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_lower_bounds, + constraint_upper_bounds, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + goto DONE; + } + + // Create solver settings + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + // Set solver parameters + status = cuOptSetFloatParameter(settings, CUOPT_MIP_ABSOLUTE_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting optimality tolerance: %d\n", status); + goto DONE; + } + + // Solve the problem + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + // Get solution information + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + + // Print results + printf("\nResults:\n"); + printf("--------\n"); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + printf("Objective value: %f\n", objective_value); + + // Get and print solution variables + cuopt_float_t* solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + if (solution_values == NULL) { + printf("Error allocating solution values\n"); + goto DONE; + } + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + free(solution_values); + goto DONE; + } + + printf("\nSolution: \n"); + for (cuopt_int_t i = 0; i < num_variables; i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + free(solution_values); + +DONE: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main() +{ + // Run the test + cuopt_int_t status = test_simple_milp(); + + if (status == CUOPT_SUCCESS) { + printf("\nTest completed successfully!\n"); + return 0; + } else { + printf("\nTest failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +## MILP Example With MPS File + +Reads a MILP from an MPS file with `cuOptReadProblem` and solves it. + +{/* embed: examples/milp_mps_example.c */} +```c +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* + * Example program for solving MPS files with cuOpt MILP solver + */ + +#include +#include +#include + +const char* termination_status_to_string(cuopt_int_t termination_status) +{ + switch (termination_status) { + case CUOPT_TERMINATION_STATUS_OPTIMAL: + return "Optimal"; + case CUOPT_TERMINATION_STATUS_INFEASIBLE: + return "Infeasible"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED: + return "Unbounded"; + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: + return "Iteration limit"; + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: + return "Time limit"; + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: + return "Numerical error"; + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: + return "Primal feasible"; + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: + return "Feasible found"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE: + return "Unbounded or infeasible"; + default: + return "Unknown"; + } +} + +cuopt_int_t solve_mps_file(const char* filename) +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + cuopt_int_t status; + cuopt_float_t time; + cuopt_int_t termination_status; + cuopt_float_t objective_value; + cuopt_int_t num_variables; + cuopt_float_t* solution_values = NULL; + + printf("Reading and solving MPS file: %s\n", filename); + + // Create the problem from MPS file + status = cuOptReadProblem(filename, &problem); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem from MPS file: %d\n", status); + goto DONE; + } + + // Get problem size + status = cuOptGetNumVariables(problem, &num_variables); + if (status != CUOPT_SUCCESS) { + printf("Error getting number of variables: %d\n", status); + goto DONE; + } + + // Create solver settings + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto DONE; + } + + // Set solver parameters + status = cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting optimality tolerance: %d\n", status); + goto DONE; + } + + // Solve the problem + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem: %d\n", status); + goto DONE; + } + + // Get solution information + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto DONE; + } + + const int has_primal_solution = + termination_status == CUOPT_TERMINATION_STATUS_OPTIMAL || + termination_status == CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE || + termination_status == CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND; + + if (has_primal_solution) { + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } + } + + // Print results + printf("\nResults:\n"); + printf("--------\n"); + printf("Number of variables: %d\n", num_variables); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); + printf("Solve time: %f seconds\n", time); + + if (has_primal_solution) { + printf("Objective value: %f\n", objective_value); + + // Get and print solution variables + solution_values = + (cuopt_float_t*)malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (solution_values == NULL) { + printf("Error allocating solution buffer\n"); + status = CUOPT_OUT_OF_MEMORY; + goto DONE; + } + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + goto DONE; + } + + printf("\nSolution: \n"); + for (cuopt_int_t i = 0; i < num_variables; i++) { + printf("x%d = %f\n", i + 1, solution_values[i]); + } + } + +DONE: + if (solution_values != NULL) { + free(solution_values); + } + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + + return status; +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + // Run the solver + cuopt_int_t status = solve_mps_file(argv[1]); + + if (status == CUOPT_SUCCESS) { + printf("\nSolver completed successfully!\n"); + return 0; + } else { + printf("\nSolver failed with status: %d\n", status); + return 1; + } +} +``` +{/* /embed */} + +Sample MILP MPS file: + +{/* embed: examples/mip_sample.mps */} +```text +* Example 2.1 from N & W +* Optimal solution -28 +NAME EXAMPLE21 +ROWS + N OBJ + L C1 + L C2 + L C3 +COLUMNS + MARK0001 'MARKER' 'INTORG' + X1 OBJ -7 + X1 C1 -1 + X1 C2 5 + X1 C3 -2 + X2 OBJ -2 + X2 C1 2 + X2 C2 1 + X2 C3 -2 + MARK0001 'MARKER' 'INTEND' +RHS + RHS C1 4 + RHS C2 20 + RHS C3 -7 +BOUNDS + UP BOUND X1 10 + UP BOUND X2 10 +ENDATA +``` +{/* /embed */} diff --git a/docs/cuopt/source/cuopt-c/quick-start.rst b/fern/docs/pages/cuopt-c/quick-start.mdx similarity index 51% rename from docs/cuopt/source/cuopt-c/quick-start.rst rename to fern/docs/pages/cuopt-c/quick-start.mdx index 73921c049f..e6b8654bde 100644 --- a/docs/cuopt/source/cuopt-c/quick-start.rst +++ b/fern/docs/pages/cuopt-c/quick-start.mdx @@ -1,16 +1,13 @@ -================= -Quickstart Guide -================= +--- +title: "Quickstart Guide" +--- NVIDIA cuOpt provides C API for LP, QP and MILP. This section will show you how to install cuOpt C API and how to use it to solve LP, QP and MILP problems. +## Installation -Installation -============ +Choose your install method below; the selector is pre-set for the C API (libcuopt), which also provides `cuopt_cli`. Copy the command and run it in your environment. See [Install](../install) for all interfaces and options. -Choose your install method below; the selector is pre-set for the C API (libcuopt), which also provides ``cuopt_cli``. Copy the command and run it in your environment. See :doc:`../install` for all interfaces and options. - -.. install-selector:: - :default-iface: c +
Please visit examples under each section to learn how to use the cuOpt C API. diff --git a/fern/docs/pages/cuopt-cli/cli-examples.mdx b/fern/docs/pages/cuopt-cli/cli-examples.mdx new file mode 100644 index 0000000000..fe338ffc48 --- /dev/null +++ b/fern/docs/pages/cuopt-cli/cli-examples.mdx @@ -0,0 +1,34 @@ +--- +title: "Examples" +--- + +## Input File Format + +`cuopt_cli` accepts both **MPS** and **LP** format input files. The format is dispatched automatically from the file extension (case-insensitive): + + - `*.lp`, `*.lp.gz`, `*.lp.bz2` → parsed as LP format + - `*.mps`, `*.mps.gz`, `*.mps.bz2` (and the equivalent `*.qps` variants) → parsed as MPS / QPS + +Any other extension (including no extension) is rejected with an error listing the supported suffixes. See `read` in `cuopt/mathematical_optimization/io/parser.hpp` (and the Python `~cuopt.linear_programming.io.Read` wrapper). + +## Basic Usage + +To solve a simple LP problem using cuopt\_cli: + +[basic\_lp\_example.sh](examples/lp/examples/basic_lp_example.sh) + +This should give you the following output: + +## Mixed Integer Programming Example + +Here's an example of solving a Mixed Integer Programming (MIP) problem using the CLI: + +[basic\_milp\_example.sh](examples/milp/examples/basic_milp_example.sh) + +This should produce output similar to: + +## Using Solver Parameters + +You can customize the solver behavior using various command line parameters. Here's a comprehensive example showing different parameter options: + +[solver\_parameters\_example.sh](examples/lp/examples/solver_parameters_example.sh) diff --git a/docs/cuopt/source/cuopt-cli/examples/lp/examples/basic_lp_example.sh b/fern/docs/pages/cuopt-cli/examples/lp/examples/basic_lp_example.sh old mode 100755 new mode 100644 similarity index 90% rename from docs/cuopt/source/cuopt-cli/examples/lp/examples/basic_lp_example.sh rename to fern/docs/pages/cuopt-cli/examples/lp/examples/basic_lp_example.sh index d5a2ab00be..f18cdbc9e3 --- a/docs/cuopt/source/cuopt-cli/examples/lp/examples/basic_lp_example.sh +++ b/fern/docs/pages/cuopt-cli/examples/lp/examples/basic_lp_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Basic LP Example using cuopt_cli diff --git a/docs/cuopt/source/cuopt-cli/examples/lp/examples/solver_parameters_example.sh b/fern/docs/pages/cuopt-cli/examples/lp/examples/solver_parameters_example.sh old mode 100755 new mode 100644 similarity index 93% rename from docs/cuopt/source/cuopt-cli/examples/lp/examples/solver_parameters_example.sh rename to fern/docs/pages/cuopt-cli/examples/lp/examples/solver_parameters_example.sh index 80e36ed5ab..36b9aa8763 --- a/docs/cuopt/source/cuopt-cli/examples/lp/examples/solver_parameters_example.sh +++ b/fern/docs/pages/cuopt-cli/examples/lp/examples/solver_parameters_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Solver Parameters Example using cuopt_cli diff --git a/docs/cuopt/source/cuopt-cli/examples/milp/examples/basic_milp_example.sh b/fern/docs/pages/cuopt-cli/examples/milp/examples/basic_milp_example.sh old mode 100755 new mode 100644 similarity index 92% rename from docs/cuopt/source/cuopt-cli/examples/milp/examples/basic_milp_example.sh rename to fern/docs/pages/cuopt-cli/examples/milp/examples/basic_milp_example.sh index 75919beca3..339e785fc6 --- a/docs/cuopt/source/cuopt-cli/examples/milp/examples/basic_milp_example.sh +++ b/fern/docs/pages/cuopt-cli/examples/milp/examples/basic_milp_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Basic MILP Example using cuopt_cli diff --git a/fern/docs/pages/cuopt-cli/index.mdx b/fern/docs/pages/cuopt-cli/index.mdx new file mode 100644 index 0000000000..33a78bd676 --- /dev/null +++ b/fern/docs/pages/cuopt-cli/index.mdx @@ -0,0 +1,7 @@ +--- +title: "Command Line Interface" +--- + +The cuopt\_cli is a command-line interface for LP/MILP solvers that accepts MPS, QPS, or LP format files as input models. The format is dispatched automatically from the file extension (case-insensitive): `.lp` (with optional `.gz` / `.bz2`) goes to the LP parser, `.mps` / `.qps` (with optional `.gz` / `.bz2`) goes to the MPS parser, and unknown extensions are rejected. It provides command-line arguments to control all solver settings and parameters when solving linear and mixed-integer programming problems. + +The cuOpt MIP solver is in **beta** and under active development. The solver currently excels at finding high-quality feasible solutions quickly with GPU-accelerated primal heuristics. Proving feasible solutions optimal remains under active development. diff --git a/fern/docs/pages/cuopt-cli/quick-start.mdx b/fern/docs/pages/cuopt-cli/quick-start.mdx new file mode 100644 index 0000000000..7a4eb2b2b1 --- /dev/null +++ b/fern/docs/pages/cuopt-cli/quick-start.mdx @@ -0,0 +1,15 @@ +--- +title: "Quickstart Guide" +--- + +cuopt\_cli is built as part of the libcuopt package. Choose your install method below; the selector is pre-set for the CLI (it uses the same libcuopt install). Copy the command and run it in your environment. See [Install](../install) for all interfaces and options. + +## Installation + +
+ +To see all available options and their descriptions: + +This will display the complete list of command-line arguments and their usage: + +Please refer to [Convex Settings](../convex-settings) and [Mip Settings](../mip-settings) for more details on default values and other options. diff --git a/fern/docs/pages/cuopt-grpc/advanced.mdx b/fern/docs/pages/cuopt-grpc/advanced.mdx new file mode 100644 index 0000000000..051177cabd --- /dev/null +++ b/fern/docs/pages/cuopt-grpc/advanced.mdx @@ -0,0 +1,126 @@ +--- +title: "Advanced Configuration" +--- + +This page lists **configuration parameters** first, then **usage** walkthroughs (TLS, Docker, private CA). Complete [Quick Start](quick-start) first (install, plain TCP server, and minimal example). + +For RPC summaries and server behavior, see [Api](api) and [Grpc Server Architecture](grpc-server-architecture). Example entry points with `CUOPT_REMOTE_*`: [Examples](examples). Contributor-only internals: `cpp/docs/grpc-server-architecture.md` in the repository. + +## Configuration Parameters + +### `cuopt_grpc_server` (host or explicit container command) + +Run `cuopt_grpc_server --help` for the full list. Typical flags (also passable inside `CUOPT_GRPC_ARGS` when using the container entrypoint): + +### NVIDIA cuOpt Container (gRPC via Entrypoint) + +These variables apply when the container **entrypoint** builds a `cuopt_grpc_server` command (see *Docker: gRPC server in container* under Usage). If you pass an explicit command after the image name, this table does not apply. + +| Variable | Default | Description | +| ------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `CUOPT_SERVER_TYPE` | *(unset)* | Set to `grpc` for entrypoint-built gRPC. Unset with no explicit command: **Python REST** server. | +| `CUOPT_SERVER_PORT` | `5001` | Passed as `--port` to `cuopt_grpc_server`. | +| `CUOPT_GPU_COUNT` | *(unset)* | When set, passed as `--workers`. When unset, `--workers` is omitted (server default, typically 1). | +| `CUOPT_GRPC_ARGS` | *(empty)* | Extra flags split on **whitespace** and appended (TLS, `--max-message-mb`, `--log-to-console`, etc.). Paths with spaces: prefer mounts without spaces or run `cuopt_grpc_server` manually with proper quoting. | + +The REST server path in the same image still uses `CUOPT_SERVER_PORT` for HTTP in other docs; that is separate from the gRPC defaults above. + +### Bundled Remote Client (Python, C API, `cuopt_cli`) + +Remote mode is active when **both** `CUOPT_REMOTE_HOST` and `CUOPT_REMOTE_PORT` are set. A **custom** gRPC client does not read these automatically; it must configure the channel and protos itself (see [Api](api)). + +| Variable | Required | Default | Description | +| ------------------------- | ---------- | ------------- | --------------------------------------------------------------------- | +| `CUOPT_REMOTE_HOST` | For remote | — | Server hostname or IP | +| `CUOPT_REMOTE_PORT` | For remote | — | Server port (e.g. `5001`) | +| `CUOPT_TLS_ENABLED` | No | `0` | Non-zero enables TLS on the client | +| `CUOPT_TLS_ROOT_CERT` | If TLS | — | PEM path to verify the **server** certificate | +| `CUOPT_TLS_CLIENT_CERT` | mTLS | — | Client certificate PEM | +| `CUOPT_TLS_CLIENT_KEY` | mTLS | — | Client private key PEM | +| `CUOPT_CHUNK_SIZE` | No | 16 MiB (lib) | Chunk size in **bytes** for large transfers (clamped in library code) | +| `CUOPT_MAX_MESSAGE_BYTES` | No | 256 MiB (lib) | Client gRPC max message size in **bytes** (clamped in library code) | +| `CUOPT_GRPC_DEBUG` | No | `0` | Non-zero: extra gRPC client logging | + +## Usage + +### Start the Server with TLS + +Basic (no TLS), plain TCP, is in [Quick Start](quick-start). Encrypted server: + +mTLS (mutual TLS): + +### How mTLS Works + +With mTLS the server verifies every client, and the client verifies the server. Trust is based on **Certificate Authorities** (CAs), not individual certificate lists: + + - `--tls-root ca.crt` tells the server which CA to trust; any client cert signed by that CA is accepted. The server does not store per-client certificates. + - `--require-client-cert` makes client verification **mandatory**. Without it, the server may still allow connections without a client cert. + - On the client, `CUOPT_TLS_ROOT_CERT` is the CA that signed the **server** certificate so the client can verify the server. + +### Restricting Access with a Private CA + +To limit which clients can connect, run your own CA and issue client certs only to authorized actors. + +**1. Create a private CA (one-time):** + +**2. Issue a client certificate:** + +Repeat for each authorized client. Keep `ca.key` private; distribute `ca.crt` to the server and per-client `client.crt` + `client.key` pairs. + +**3. Issue a server certificate (same CA):** + +`server.crt` must be signed by the CA you give to clients, and **subjectAltName** must match the hostname or IP clients use. gRPC hostname verification expects SAN; **CN alone is not sufficient**. + +**4. Start the server:** + +**5. Configure an authorized client:** + +**Revocation:** built-in gRPC TLS does **not** implement CRL or OCSP. To revoke a client, rotate the CA, stop issuing from a compromised CA, or terminate TLS at a reverse proxy (e.g., Envoy) that supports revocation. + +### Docker: gRPC Server in Container + +The official NVIDIA cuOpt image includes the REST server and `cuopt_grpc_server`. The entrypoint behaves as follows: + +1. **Explicit command** after the image name (e.g. `cuopt_grpc_server …`) runs as-is; env-based gRPC wiring is skipped. +2. **\`CUOPT\_SERVER\_TYPE=grpc\`** builds a `cuopt_grpc_server` command from the **NVIDIA cuOpt container** table in *Configuration parameters*. +3. **Default** — if `CUOPT_SERVER_TYPE` is unset and there is no explicit command, the Python **REST** server starts. + + +Examples use `--gpus all`. That requires NVIDIA GPUs on the host and Docker with the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) so devices are visible inside the container. + + +Typical run: + +TLS example with a cert volume: + +Bypass the entrypoint: + +### Client Environment (Examples) + +**Required** for remote (see *Bundled remote client* table for all variables): + +**TLS** (optional): + +For mTLS, also: + +## Limitations and Scope + + - **Problem types** — **LP**, **MILP**, and **QP** are supported on the gRPC remote path. **Routing** (VRP, TSP, PDP) is **not** supported yet; use the [REST self-hosted server](../cuopt-server/index) for remote routing until a future release adds routing over `CuOptRemoteService`. + - **Message size** — Large problems use chunking; very large models can still hit gRPC max message / timeout limits. Tune `CUOPT_CHUNK_SIZE`, `CUOPT_MAX_MESSAGE_BYTES`, server `--max-message-mb`, and solver `time_limit` as needed. + - **\`\`CUOPT\_GRPC\_ARGS\`\`** — Parsed on whitespace only; arguments containing spaces are awkward unless you invoke `cuopt_grpc_server` directly. + - **CRL / OCSP** — Not handled by the bundled gRPC TLS stack; use a private CA rotation strategy or a TLS-terminating proxy if you need revocation workflows. + +## Troubleshooting + +| Symptom | Check | +| ------------------------- | ---------------------------------------------------------------------------------------------- | +| Connection refused | Server running; host/port match; firewalls and Docker port mapping. | +| TLS handshake failure | `CUOPT_TLS_ENABLED=1`; correct CA and cert paths; SAN matches server name. | +| Cannot open TLS file | Path exists and is readable inside the client/server environment (including container mounts). | +| Timeout on large problems | Increase solver `time_limit` and client/server message limits. | + +## Further Reading + + - [Quick Start](quick-start) — Plain TCP quick path. + - [Examples](examples) — Links to Python, C, and CLI example sections (use with `CUOPT_REMOTE_*` on the client). + - [Grpc Server Architecture](grpc-server-architecture) — Process model and job behavior (operator overview). diff --git a/fern/docs/pages/cuopt-grpc/api.mdx b/fern/docs/pages/cuopt-grpc/api.mdx new file mode 100644 index 0000000000..2757ef2929 --- /dev/null +++ b/fern/docs/pages/cuopt-grpc/api.mdx @@ -0,0 +1,57 @@ +--- +title: "gRPC API (Reference)" +--- + +The **CuOptRemoteService** gRPC API is defined in Protocol Buffers under the `cuopt.remote` package. Source files in the repository: + + - `cpp/src/grpc/cuopt_remote_service.proto` — service and job/chunk/log RPCs + - `cpp/src/grpc/cuopt_remote.proto` — LP/MIP problem, settings, and result messages + +Most users do **not** call these RPCs directly: the NVIDIA cuOpt **Python** API, **C API**, and **cuopt\_cli** submit jobs using solver APIs plus [environment variables](advanced). **Custom** clients call `CuOptRemoteService` over gRPC using these definitions. This page summarizes the service for custom integrators and debugging. + +## Service: `CuOptRemoteService` + +### Asynchronous Jobs + +| RPC | Purpose | +| ------------------- | ----------------------------------------------------------------------------- | +| `SubmitJob` | Submit an LP or MILP job in one message (within gRPC message size limits). | +| `CheckStatus` | Poll job status by `job_id`. | +| `GetResult` | Fetch a completed result (unary, when the payload fits one message). | +| `DeleteResult` | Remove a stored result from server memory. | +| `CancelJob` | Cancel a queued or running job. | +| `WaitForCompletion` | Block until the job finishes (status only; use `GetResult` for the solution). | + +### Chunked Upload (Large Problems) + +| RPC | Purpose | +| --------------------- | ------------------------------------------------------------------------------ | +| `StartChunkedUpload` | Begin a session; send problem metadata and settings (arrays follow as chunks). | +| `SendArrayChunk` | Upload one slice of a numeric array field. | +| `FinishChunkedUpload` | Finalize the upload and return `job_id` (same as `SubmitJob`). | + +### Chunked Download (Large Results) + +| RPC | Purpose | +| ----------------------- | ----------------------------------------------------------------------------- | +| `StartChunkedDownload` | Begin a download session; returns scalar result fields and array descriptors. | +| `GetResultChunk` | Fetch one chunk of a result array. | +| `FinishChunkedDownload` | End the download session and release server state. | + +### Streaming and Callbacks + +| RPC | Purpose | +| --------------- | --------------------------------------------- | +| `StreamLogs` | Server-streaming solver log lines for a job. | +| `GetIncumbents` | MILP incumbent solutions since a given index. | + +## Messages and Constraints + + - **Problem types** — LP and MILP in the enum; the problem payload can include quadratic objective data for **QP**-style solves where the client API supports it. **Routing** over this gRPC service is **not** available yet; it is planned for an **upcoming** release (use REST for remote routing today). + - **Solver settings** — Carried as `PDLPSolverSettings` or `MIPSolverSettings` inside the request or chunked header, aligned with the NVIDIA cuOpt solver options documentation. + - **Errors** — gRPC status codes carry failures (see comments at the end of `cuopt_remote_service.proto`). + +## Further Reading + + - [Grpc Server Architecture](grpc-server-architecture) — Server process model and job lifecycle (overview); [Advanced](advanced) for `cuopt_grpc_server` flags. Contributor details: `cpp/docs/grpc-server-architecture.md`. + - [Advanced](advanced) — TLS, Docker, client environment variables, and limitations. diff --git a/fern/docs/pages/cuopt-grpc/examples.mdx b/fern/docs/pages/cuopt-grpc/examples.mdx new file mode 100644 index 0000000000..c6355ff254 --- /dev/null +++ b/fern/docs/pages/cuopt-grpc/examples.mdx @@ -0,0 +1,51 @@ +--- +title: "Examples" +--- + +gRPC remote execution uses the same **Python**, **C API**, and **cuopt\_cli** entry points as a local solve. After you start `cuopt_grpc_server` on the GPU host ([Quick Start](quick-start)), set the client environment and run **any** of the examples below **unchanged** — no code edits are required. + +On the **client** host, before running the example commands or scripts: + +Add TLS or tuning variables from [Advanced](advanced) if your deployment uses them. + + +Routing solve over gRPC is not supported. For solving routing problems remotely today, use the HTTP/JSON [REST self-hosted server](../cuopt-server/index) and [Examples](../cuopt-server/examples/index). + + +## Where to Find Examples + +### Python (LP / QP / MILP) + + - [Convex Examples](../cuopt-python/convex/convex-examples) — runnable Python samples (LP, QP). With `CUOPT_REMOTE_HOST` and `CUOPT_REMOTE_PORT` set on the client, solves go to the remote server automatically. + - [Mip Examples](../cuopt-python/mip/mip-examples) — runnable Python samples (MILP). With `CUOPT_REMOTE_HOST` and `CUOPT_REMOTE_PORT` set on the client, solves go to the remote server automatically. + +### C API (LP / QP / MILP) + + - [Convex Examples](../cuopt-c/convex/convex-examples) — LP and QP C examples. + + - [Mip Examples](../cuopt-c/mip/mip-examples) — MILP C examples. + + Compile and run these programs with the same exports in the shell; `solve_lp` / `solve_mip` use gRPC when both remote variables are set (see [Convex C Api](../cuopt-c/convex/convex-c-api) for API reference). + +### `cuopt_cli` + + - [Cli Examples](../cuopt-cli/cli-examples) — `cuopt_cli` invocations. With the exports above, the CLI forwards solves to `cuopt_grpc_server`. + +### Minimal Demos (This Section) + +Bundled with the gRPC docs source for a quick copy-paste path (also walked through in [Quick Start](quick-start)): + + - [remote\_lp\_demo.py](examples/remote_lp_demo.py) + - [remote\_lp\_demo.mps](examples/remote_lp_demo.mps) + +### Custom gRPC Client + +Integrations that do **not** use the bundled Python / C / CLI stack should speak `CuOptRemoteService` directly. See [Api](api), [Grpc Server Architecture](grpc-server-architecture), and `cpp/docs/grpc-server-architecture.md` in the repository for protos and server behavior. + +## More Samples + + - [NVIDIA cuOpt examples on GitHub](https://github.com/NVIDIA/cuopt-examples) — set the remote environment on the **client** before running notebooks or scripts. + +## REST vs gRPC + + - **Self-hosted HTTP/JSON** — [Index](../cuopt-server/examples/index) targets the REST server; request shapes follow the OpenAPI workflow, not the `CuOptRemoteService` protos. diff --git a/docs/cuopt/source/cuopt-grpc/examples/remote_lp_demo.mps b/fern/docs/pages/cuopt-grpc/examples/remote_lp_demo.mps similarity index 100% rename from docs/cuopt/source/cuopt-grpc/examples/remote_lp_demo.mps rename to fern/docs/pages/cuopt-grpc/examples/remote_lp_demo.mps diff --git a/docs/cuopt/source/cuopt-grpc/examples/remote_lp_demo.py b/fern/docs/pages/cuopt-grpc/examples/remote_lp_demo.py similarity index 100% rename from docs/cuopt/source/cuopt-grpc/examples/remote_lp_demo.py rename to fern/docs/pages/cuopt-grpc/examples/remote_lp_demo.py diff --git a/docs/cuopt/source/cuopt-grpc/grpc-server-architecture.md b/fern/docs/pages/cuopt-grpc/grpc-server-architecture.mdx similarity index 79% rename from docs/cuopt/source/cuopt-grpc/grpc-server-architecture.md rename to fern/docs/pages/cuopt-grpc/grpc-server-architecture.mdx index 4054ee3abd..6b1e9af5d3 100644 --- a/docs/cuopt/source/cuopt-grpc/grpc-server-architecture.md +++ b/fern/docs/pages/cuopt-grpc/grpc-server-architecture.mdx @@ -1,4 +1,6 @@ -# gRPC Server Behavior +--- +title: "gRPC Server Behavior" +--- NVIDIA cuOpt's **`cuopt_grpc_server`** uses one **main process** (gRPC front end, job tracking, background threads) and **worker processes** that run GPU solves. That layout gives isolation between jobs, optional parallelism when you set multiple workers, and streaming for large problems and logs. @@ -6,15 +8,15 @@ Implementation details (IPC layout, C++ source map, chunked transfer internals) ## Process model -![gRPC Server Process Model](images/grpc-process-model.png) +![gRPC Server Process Model](/docs/images/grpc-process-model.png) ## Job lifecycle (summary) -**Submit** → the server assigns a job id and queues work. **Process** → a worker pulls the problem, solves on the GPU, and streams the result back. **Retrieve** → the client uses status and result RPCs (including chunked download when needed). See [gRPC API (reference)](api.rst) for RPC names. +**Submit** → the server assigns a job id and queues work. **Process** → a worker pulls the problem, solves on the GPU, and streams the result back. **Retrieve** → the client uses status and result RPCs (including chunked download when needed). See [gRPC API (reference)](api) for RPC names. ## Job states -![gRPC Server Job States](images/grpc-job-states.png) +![gRPC Server Job States](/docs/images/grpc-job-states.png) ## Logs, capacity, and workers @@ -31,6 +33,6 @@ Implementation details (IPC layout, C++ source map, chunked transfer internals) ## Further reading -- [Advanced configuration](advanced.rst) — `cuopt_grpc_server` **command-line flags**, TLS, Docker (`CUOPT_SERVER_TYPE`, `CUOPT_GRPC_ARGS`), and **client** environment variables (authoritative for operators). -- [gRPC API (reference)](api.rst) — `CuOptRemoteService` RPC overview. +- [Advanced configuration](advanced) — `cuopt_grpc_server` **command-line flags**, TLS, Docker (`CUOPT_SERVER_TYPE`, `CUOPT_GRPC_ARGS`), and **client** environment variables (authoritative for operators). +- [gRPC API (reference)](api) — `CuOptRemoteService` RPC overview. - **Contributor reference** — `cpp/docs/grpc-server-architecture.md` in the repository (IPC, source files, streaming, threading). diff --git a/fern/docs/pages/cuopt-grpc/index.mdx b/fern/docs/pages/cuopt-grpc/index.mdx new file mode 100644 index 0000000000..73eef980aa --- /dev/null +++ b/fern/docs/pages/cuopt-grpc/index.mdx @@ -0,0 +1,13 @@ +--- +title: "gRPC Remote Execution" +--- + +**NVIDIA cuOpt gRPC remote execution** runs optimization solves on a remote GPU host. Clients can be the **Python** API, **C API**, **\`cuopt\_cli\`**, or a **custom** program that speaks `CuOptRemoteService` over gRPC. For Python, the C API, and `cuopt_cli`, set `CUOPT_REMOTE_HOST` and `CUOPT_REMOTE_PORT` to forward solves to `cuopt_grpc_server`. + + +**Problem types (gRPC remote):** LP, MILP, and QP are supported today. **Routing** (VRP, TSP, PDP, and related APIs) over gRPC remote execution is **not** available yet; support is planned for an **upcoming** release. For routing against a remote service today, use the HTTP/JSON [REST self-hosted server](../cuopt-server/index). + + +This is **not** the HTTP/JSON [REST self-hosted server](../cuopt-server/index) (FastAPI). REST is for arbitrary HTTP clients; gRPC is for the bundled remote client in NVIDIA cuOpt's native APIs. + +Start with [Quick Start](quick-start) (install selector, how remote execution works, Docker, and a minimal LP example). Use [Advanced](advanced) for TLS, tuning, limitations, and troubleshooting; [Examples](examples) for additional patterns. diff --git a/fern/docs/pages/cuopt-grpc/quick-start.mdx b/fern/docs/pages/cuopt-grpc/quick-start.mdx new file mode 100644 index 0000000000..43e36daa01 --- /dev/null +++ b/fern/docs/pages/cuopt-grpc/quick-start.mdx @@ -0,0 +1,86 @@ +--- +title: "Quick Start" +--- + +**NVIDIA cuOpt gRPC remote execution** runs LP, MILP, and QP solves on a **GPU host** while your **Python** code, **C API** program, **\`cuopt\_cli\`**, or a **custom** client runs elsewhere. When you set `CUOPT_REMOTE_HOST` and `CUOPT_REMOTE_PORT`, the bundled **Python**, **C API**, and **cuopt\_cli** clients forward `solve_lp` / `solve_mip` to `cuopt_grpc_server` with **no code changes**. **Custom** clients call `CuOptRemoteService` directly (see [Api](api)). + + +**Problem types (gRPC remote):** **LP**, **MILP**, and **QP** are supported today. **Routing** (VRP, TSP, PDP) over this path is **not** available; For remote routing, use the HTTP/JSON [REST self-hosted server](../cuopt-server/index). This guide is **not** the REST server—see [Index](../cuopt-server/index) for HTTP/JSON. + + +## How Remote Execution Works + +1. **GPU host** — Run `cuopt_grpc_server` (bare metal or in the official container) so it listens on a TCP port (default **5001**). +2. **Client** — Install the NVIDIA cuOpt client libraries on the machine where you invoke the solver. Set `CUOPT_REMOTE_HOST` to that GPU host’s address and `CUOPT_REMOTE_PORT` to the listen port. +3. **Solve** — Call the same APIs you would for a local solve. The client library opens a gRPC channel, streams the problem, and retrieves the result. Unset the two variables to solve **locally** again (local mode still needs a GPU on that machine where applicable). + +## Install NVIDIA cuOpt + +Use the selector below on the **GPU server** and on **clients** that need Python, the C API, or `cuopt_cli`. It is pre-set to **C (libcuopt)** because that bundle ships `cuopt_grpc_server`, `cuopt_cli`, and libraries together; switch to **Python** if you only need Python packages on a lightweight client. + +
+ +Verify the server binary after install: + +For the same install selector with **Container** / registry choices (Docker Hub or NGC), see [Install](../install). + +## Run the gRPC Server (GPU Host) + +**Bare metal** — after activating the same environment you used to install NVIDIA cuOpt: + +Leave the process running. Default port **5001**; change `--port` if needed and expose the same port on the client side. + +**Docker** — requires [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) (or equivalent) on the host. Pull an image tag from [Install](../install) or the **Container** row in the selector above; substitute `` below. + +Entrypoint mode (recommended when you are not passing an explicit command): + +Or invoke the binary explicitly: + + +The container image defaults to the Python **REST** server when `CUOPT_SERVER_TYPE` is unset and you do not override the command; setting `CUOPT_SERVER_TYPE=grpc` selects `cuopt_grpc_server`. Extra environment variables (`CUOPT_SERVER_PORT`, `CUOPT_GPU_COUNT`, `CUOPT_GRPC_ARGS`) and TLS are documented in [Advanced configuration](advanced). + + +## Point the Client at the Server + +On the machine where you run Python, the C API, or `cuopt_cli` (use `127.0.0.1` if the server is on the same host): + +Optional TLS and tuning variables are in [Advanced](advanced). + +## Minimal Python Example (LP) + +The script is the same for **local** or **remote** solves: with the exports above, the client library forwards to `cuopt_grpc_server`; without them, the solve runs locally (where a GPU is available). Please make sure the server is running before running the client. + +[remote\_lp\_demo.py](examples/remote_lp_demo.py) + +Run the script from your NVIDIA cuOpt Python environment. From a **repository checkout** (repo root): + +Or, after [downloading](examples/remote_lp_demo.py) the file into your current directory: + +You should see an optimal termination. To solve **locally**, unset the remote variables and rerun with the **same** path you used above: + +## Minimal `cuopt_cli` Example (LP) + +The same **LP** is available as MPS. With `CUOPT_REMOTE_HOST` and `CUOPT_REMOTE_PORT` set as above, `cuopt_cli` forwards the solve to the remote server; unset them for a **local** run (GPU on that machine). Please make sure the server is running before running the client. + +[remote\_lp\_demo.mps](examples/remote_lp_demo.mps) + +From a **repository checkout** (repo root): + +Or, after [downloading](examples/remote_lp_demo.mps) the MPS into your current directory: + +To solve **locally** with the same file: + +More options (time limits, relaxation): [Quick Start](../cuopt-cli/quick-start) and [Examples](examples). + +**C API** — With the same environment variables set, call `solve_lp` / `solve_mip` as in [Convex C Api](../cuopt-c/convex/convex-c-api). + +More patterns (MPS variants, custom gRPC): [Examples](examples). + +## Next Steps + + - [Install](../install) — Top-level install selector (all interfaces), including **Container** pulls. + - [Advanced](advanced) — TLS / mTLS, Docker environment reference, tuning, limitations, troubleshooting. + - [Examples](examples) — Additional client examples and links to LP/MILP sample collections. + - [Api](api) and [Grpc Server Architecture](grpc-server-architecture) — RPC summary and server behavior overview. + +See [System Requirements](../system-requirements) for GPU, CUDA, and OS requirements. diff --git a/fern/docs/pages/cuopt-python/convex/convex-api.mdx b/fern/docs/pages/cuopt-python/convex/convex-api.mdx new file mode 100644 index 0000000000..ed8e927445 --- /dev/null +++ b/fern/docs/pages/cuopt-python/convex/convex-api.mdx @@ -0,0 +1,2066 @@ +--- +title: "Convex Optimization Python API Reference" +--- + + +## Problem + +
+ +### class `VType` + +Type of an LP/MIP variable. + +**Attributes** + +- **`CONTINUOUS`** (`str`) — Continuous variable (default). Equivalent to the module-level constant ``CONTINUOUS``. +- **`INTEGER`** (`str`) — Integer variable. Equivalent to the module-level constant ``INTEGER``. +- **`SEMI_CONTINUOUS`** (`str`) — Semi-continuous variable: either zero or within a given range. Equivalent to the module-level constant ``SEMI_CONTINUOUS``. + + +**Members:** `CONTINUOUS`, `INTEGER`, `SEMI_CONTINUOUS` + +
+ +### class `CType` + +Sense of a constraint. + +**Attributes** + +- **`LE`** (`str`) — Less-than-or-equal (≤). Equivalent to the module-level constant ``LE``. +- **`GE`** (`str`) — Greater-than-or-equal (≥). Equivalent to the module-level constant ``GE``. +- **`EQ`** (`str`) — Equality (=). Equivalent to the module-level constant ``EQ``. + + +**Members:** `LE`, `GE`, `EQ` + +
+ +### class `sense` + +Objective sense of a model. + +**Attributes** + +- **`MINIMIZE`** (`int`) — Minimize the objective function (default). Equivalent to the module-level constant ``MINIMIZE``. +- **`MAXIMIZE`** (`int`) — Maximize the objective function. Equivalent to the module-level constant ``MAXIMIZE``. + + +**Members:** `MAXIMIZE`, `MINIMIZE` + +
+ +### class `Variable` + +cuOpt variable object initialized with details of the variable such as lower bound, upper bound, type and name. Variables are always associated with a problem and can be created using :py:meth:`Problem.addVariable`. + +**Parameters** + +- **`lb`** (`float`) — Lower bound of the variable. Defaults to 0. +- **`ub`** (`float`) — Upper bound of the variable. Defaults to infinity. +- **`vtype`** (`enum`) — CONTINUOUS or INTEGER. Defaults to CONTINUOUS. +- **`obj`** (`float`) — Coefficient of the Variable in the objective. +- **`name`** (`str`) — Name of the variable. Optional. + +**Attributes** + +- **`VariableName`** (`str`) — Name of the Variable. +- **`VariableType`** (`CONTINUOUS, INTEGER, or SEMI_CONTINUOUS`) — Variable type. +- **`LB`** (`float`) — Lower Bound of the Variable. +- **`UB`** (`float`) — Upper Bound of the Variable. +- **`Obj`** (`float`) — Coefficient of the variable in the Objective function. +- **`Value`** (`float`) — Value of the variable after solving. +- **`ReducedCost`** (`float`) — Reduced Cost after solving an LP problem. +- **`MIPStart`** (`float`) — Initial value (warm-start hint) for the variable. Defaults to NaN (unset). Only used for a MIP problem. + + +#### def `__init__` + +```python +__init__(lb = 0.0, ub = float('inf'), obj = 0.0, vtype = CONTINUOUS, vname = '') +``` + +#### def `getIndex` + +```python +getIndex() +``` + +Get the index position of the variable in the problem. + + +#### def `getValue` + +```python +getValue() +``` + +Returns the Value of the variable computed in current solution. Defaults to 0 + + +#### def `getObjectiveCoefficient` + +```python +getObjectiveCoefficient() +``` + +Returns the objective coefficient of the variable. + + +#### def `setObjectiveCoefficient` + +```python +setObjectiveCoefficient(val) +``` + +Sets the objective cofficient of the variable. + + +#### def `setLowerBound` + +```python +setLowerBound(val) +``` + +Sets the lower bound of the variable. + + +#### def `getLowerBound` + +```python +getLowerBound() +``` + +Returns the lower bound of the variable. + + +#### def `setUpperBound` + +```python +setUpperBound(val) +``` + +Sets the upper bound of the variable. + + +#### def `getUpperBound` + +```python +getUpperBound() +``` + +Returns the upper bound of the variable. + + +#### def `setVariableType` + +```python +setVariableType(val) +``` + +Sets the variable type of the variable. Variable types can be CONTINUOUS, INTEGER, or SEMI_CONTINUOUS. + + +#### def `getVariableType` + +```python +getVariableType() +``` + +Returns the type of the variable. + + +#### def `setVariableName` + +```python +setVariableName(val) +``` + +Sets the name of the variable. + + +#### def `getVariableName` + +```python +getVariableName() +``` + +Returns the name of the variable. + + +#### def `setMIPStart` + +```python +setMIPStart(val) +``` + +Sets the MIP start (initial primal solution hint) value for the variable. Use ``float("nan")`` to unset. + + +#### def `getMIPStart` + +```python +getMIPStart() +``` + +Returns the MIP start (initial primal solution hint) value of the variable. Defaults to NaN when unset. + + +
+ +### class `QuadraticExpression` + +QuadraticExpressions contain quadratic terms, linear terms, and a constant. Use them for quadratic objectives (``Problem.setObjective``) or quadratic constraints via ``<=`` or ``>=`` comparisons passed to :py:meth:`Problem.addConstraint` (equality is not supported). QuadraticExpressions can be added and subtracted with other QuadraticExpressions, LinearExpressions, and Variables, and can also be multiplied and divided by scalars. + +**Parameters** + +- **`qmatrix`** (`List[List[float]] or 2D numpy array.`) — Matrix containing quadratic coefficient matrix terms. Should be a square matrix with shape as (num_vars, num_vars). +- **`qvars`** (`List[Variable]`) — List of variables denoting the rows and cols in qmatrix. It is a mandatory field when providing qmatrix. qvars should be in the order of variables added to the problem and can be obtained using problem.getVariables(). The length of qvars should be equal to length of row/col in qmatrix. +- **`qvars1`** (`List[Variable]`) — List of first variables for quadratic terms. This should be used if adding quadratic terms in triplet (i, j, x) format where i is the row variable, j is the column variable and x is the corresponding coefficient. qvars1 contains all i variables representing the row. +- **`qvars2`** (`List[Variable]`) — List of second variables for quadratic terms. This should be used if adding quadratic terms in triplet (i, j, x) format where i is the row variable, j is the column variable and x is the corresponding coefficient. qvars2 contains all j variables representing the column. +- **`qcoefficients`** (`List[float]`) — List of coefficients for the quadratic terms. This should be used if adding quadratic terms in triplet (i, j, x) format where i is the row variable, j is the column variable and x is the corresponding coefficient. qcoefficients contains all x values representing coefficients for (i,j) +- **`vars`** (`List[Variable]`) — List of Variables for linear terms. +- **`coefficients`** (`List[float]`) — List of coefficients for linear terms. +- **`constant`** (`float`) — Constant of the quadratic expression. + +**Examples** + +```python +>>> x = problem.addVariable() +>>> y = problem.addVariable() +>>> # Create objective x^2 + 2*x*y + 3*x + 4 using matrix +>>> quad_matrix = QuadraticExpression( +... qmatrix=[[1.0, 2.0], [0.0, 0.0]], +... qvars=[x, y] +... ) +>>> quad_obj_using_matrix = quad_matrix + 3*x + 4 +>>> # Create objective x^2 + 2*x*y + 3*x + 4 using expression +>>> quad_obj_using_expr = x*x + 2*x*y + 3*x + 4 +``` + + +#### def `__init__` + +```python +__init__(qmatrix = None, qvars = [], qvars1 = [], qvars2 = [], qcoefficients = [], vars = [], coefficients = [], constant = 0.0) +``` + +#### def `getVariables` + +```python +getVariables() +``` + +Returns all the quadractic variables in the expression as list of tuples containing Variable 1 and Variable 2 for each term. + + +#### def `getVariable1` + +```python +getVariable1(i) +``` + +Gets first Variable at ith index in the quadratic term. + + +#### def `getVariable2` + +```python +getVariable2(i) +``` + +Gets second Variable at ith index in the quadratic term. + + +#### def `getCoefficients` + +```python +getCoefficients() +``` + +Returns all the coefficients of the quadratic term. + + +#### def `getCoefficient` + +```python +getCoefficient(i) +``` + +Gets the coefficient of the quadratic term at ith index. + + +#### def `getLinearExpression` + +```python +getLinearExpression() +``` + +Returns the Linear Expression associated with the Quadratic Expression. + + +#### def `getValue` + +```python +getValue() +``` + +Returns the value of the expression computed with the current solution. + + +
+ +### class `LinearExpression` + +LinearExpressions contain a set of variables, the coefficients for the variables, and a constant. LinearExpressions can be used to create constraints and the objective in the Problem. LinearExpressions can be added and subtracted with other LinearExpressions and Variables and can also be multiplied and divided by scalars. LinearExpressions can be compared with scalars, Variables, and other LinearExpressions to create Constraints. + +**Parameters** + +- **`vars`** (`List`) — List of Variables in the linear expression. +- **`coefficients`** (`List`) — List of coefficients corresponding to the variables. +- **`constant`** (`float`) — Constant of the linear expression. + + +#### def `__init__` + +```python +__init__(vars, coefficients, constant) +``` + +#### def `getVariables` + +```python +getVariables() +``` + +Returns all the variables in the linear expression. + + +#### def `getVariable` + +```python +getVariable(i) +``` + +Gets Variable at ith index in the linear expression. + + +#### def `getCoefficients` + +```python +getCoefficients() +``` + +Returns all the coefficients in the linear expression. + + +#### def `getCoefficient` + +```python +getCoefficient(i) +``` + +Gets the coefficient of the variable at ith index of the linear expression. + + +#### def `getConstant` + +```python +getConstant() +``` + +Returns the constant in the linear expression. + + +#### def `zipVarCoefficients` + +```python +zipVarCoefficients() +``` + +#### def `getValue` + +```python +getValue() +``` + +Returns the value of the expression computed with the current solution. + + +
+ +### class `Constraint` + +cuOpt constraint object containing a linear or quadratic (QCMATRIX) expression, the sense of the constraint, and the right-hand side. Constraints are associated with a problem and can be created using :py:meth:`Problem.addConstraint`. + +**Parameters** + +- **`expr`** (`LinearExpression or QuadraticExpression`) — Expression corresponding to the constraint. +- **`sense`** (`enum`) — Sense of the constraint. Either LE for <=, GE for >= or EQ for == . +- **`rhs`** (`float`) — Constraint right-hand side value. +- **`name`** (`str, Optional`) — Name of the constraint. Optional. + +**Attributes** + +- **`ConstraintName`** (`str`) — Name of the constraint. +- **`Sense`** (`LE, GE or EQ`) — Row sense. LE for <=, GE for >= or EQ for == . +- **`RHS`** (`float`) — Constraint right-hand side value (linear rows). +- **`is_quadratic`** (`bool`) — True when the row is exported as a QCMATRIX quadratic constraint. +- **`Slack`** (`float`) — Computed LHS - RHS with current solution. +- **`DualValue`** (`float`) — Constraint dual value in the current solution. + + +#### def `__init__` + +```python +__init__(expr, sense, rhs, name = '') +``` + +#### def `getConstraintName` + +```python +getConstraintName() +``` + +Returns the name of the constraint. + + +#### def `getSense` + +```python +getSense() +``` + +Returns the sense of the constraint. Constraint sense can be LE(<=), GE(>=) or EQ(==). + + +#### def `getRHS` + +```python +getRHS() +``` + +Returns the right-hand side value of the constraint. + + +#### def `getCoefficient` + +```python +getCoefficient(var) +``` + +Returns the coefficient of a variable in the constraint. + + +#### def `compute_slack` + +```python +compute_slack() +``` + +
+ +### class `Problem` + +A Problem defines a Linear Program or Mixed Integer Program Variable can be be created by calling addVariable() Constraints can be added by calling addConstraint() The objective can be set by calling setObjective() The problem data is formed when calling solve(). + +**Parameters** + +- **`model_name`** (`str, optional`) — Name of the model. Default is an empty string. + +**Attributes** + +- **`Name`** (`str`) — Name of the model. +- **`ObjSense`** (`sense`) — Objective sense (MINIMIZE or MAXIMIZE). +- **`ObjConstant`** (`float`) — Constant term in the objective. +- **`Status`** (`int`) — Status of the problem after solving. +- **`SolveTime`** (`float`) — Time taken to solve the problem. +- **`SolutionStats`** (`object`) — Solution statistics for LP or MIP problem. +- **`ObjValue`** (`float`) — Objective value of the problem. +- **`IsMIP`** (`bool`) — Indicates if the problem is a Mixed Integer Program. +- **`NumVariables`** (`int`) — Number of Variables in the problem. +- **`NumConstraints`** (`int`) — Number of constraints in the problem. +- **`NumNZs`** (`int`) — Number of non-zeros in the problem. + +**Examples** + +```python +>>> problem = problem.Problem("MIP_model") +>>> x = problem.addVariable(lb=-2.0, ub=8.0, vtype=INTEGER) +>>> y = problem.addVariable(name="Var2") +>>> problem.addConstraint(2*x - 3*y <= 10, name="Constr1") +>>> expr = 3*x + y +>>> problem.addConstraint(expr + x == 20, name="Constr2") +>>> problem.setObjective(x + y, sense=MAXIMIZE) +>>> problem.solve() +``` + + +#### def `__init__` + +```python +__init__(model_name = '') +``` + +#### def `update` + +```python +update() +``` + +Update the problem. This is mandatory if attributes of existing Variables, Constraints or Objective has been modified. + + +#### def `reset_solved_values` + +```python +reset_solved_values() +``` + +#### def `addVariable` + +```python +addVariable(lb = 0.0, ub = float('inf'), obj = 0.0, vtype = CONTINUOUS, name = '') +``` + +Adds a variable to the problem defined by lower bound, upper bound, type and name. + +**Parameters** + +- **`lb`** (`float`) — Lower bound of the variable. Defaults to 0. +- **`ub`** (`float`) — Upper bound of the variable. Defaults to infinity. +- **`vtype`** (`enum :py:class:`VType``) — vtype.CONTINUOUS, vtype.INTEGER, or vtype.SEMI_CONTINUOUS. Defaults to CONTINUOUS. +- **`name`** (`string`) — Name of the variable. Optional. + +**Returns** + +- **`variable`** (`:py:class:`Variable``) — Variable object added to the problem. + +**Examples** + +```python +>>> problem = problem.Problem("MIP_model") +>>> x = problem.addVariable(lb=-2.0, ub=8.0, vtype=INTEGER, + name="Var1") +``` + + +#### def `addConstraint` + +```python +addConstraint(constr, name = '') +``` + +Adds a constraint to the problem defined by constraint object and name. A constraint is generated using LinearExpression or QuadraticExpression comparisons (``<=``, ``>=``, or ``==``). + +**Parameters** + +- **`constr`** (`:py:class:`Constraint``) — Constructed using expression comparisons (see Examples). +- **`name`** (`string`) — Name of the constraint. Optional. + +**Examples** + +```python +>>> problem = problem.Problem("MIP_model") +>>> x = problem.addVariable(lb=-2.0, ub=8.0, vtype=INTEGER) +>>> y = problem.addVariable(name="Var2") +>>> problem.addConstraint(2*x - 3*y <= 10, name="Constr1") +>>> expr = 3*x + y +>>> problem.addConstraint(expr + x == 20, name="Constr2") +>>> problem.addConstraint(-x*x + y*y <= 0, name="soc") +``` + + +#### def `updateConstraint` + +```python +updateConstraint(constr, coeffs = [], rhs = None) +``` + +Updates a previously added constraint. Values that can be updated are constraint coefficients and RHS. + +**Parameters** + +- **`constr`** (`:py:class:`Constraint``) — Constraint to be updated. +- **`coeffs`** (`List[Tuple[:py:class:`Variable`, coefficient]]`) — List of Tuples containing variable and corresponding coefficient. Optional. +- **`rhs`** (`int|float`) — New RHS value for the constraint. + +**Examples** + +```python +>>> problem = problem.Problem("MIP_model") +>>> x = problem.addVariable(lb=0.0, vtype=INTEGER) +>>> y = problem.addVariable(lb=0.0, vtype=INTEGER) +>>> c1 = problem.addConstraint(2 * x + y <= 7, name="c1") +>>> c2 = problem.addConstraint(x + y <= 5, name="c2") +>>> problem.updateConstraint(c1, coeffs=[(x, 1)], rhs=10) +``` + + +#### def `setObjective` + +```python +setObjective(expr, sense = MINIMIZE) +``` + +Set the Objective of the problem with an expression that needs to be MINIMIZED or MAXIMIZED. + +**Parameters** + +- **`expr`** (`:py:class:`LinearExpression` or :py:class:`Variable` or Constant`) — Objective expression that needs maximization or minimization. +- **`sense`** (`enum :py:class:`sense``) — Sets whether the problem is a maximization or a minimization problem. Values passed can either be MINIMIZE or MAXIMIZE. Defaults to MINIMIZE. + +**Examples** + +```python +>>> problem = problem.Problem("MIP_model") +>>> x = problem.addVariable(lb=-2.0, ub=8.0, vtype=INTEGER) +>>> y = problem.addVariable(name="Var2") +>>> problem.addConstraint(2*x - 3*y <= 10, name="Constr1") +>>> expr = 3*x + y +>>> problem.addConstraint(expr + x == 20, name="Constr2") +>>> problem.setObjective(x + y, sense=MAXIMIZE) +``` + + +#### def `updateObjective` + +```python +updateObjective(coeffs = [], constant = None, sense = None) +``` + +Updates the objective of the problem. Values that can be updated are objective coefficients, constant and sense. + +**Parameters** + +- **`coeffs`** (`List[Tuple[:py:class:`Variable`, coefficient]]`) — List of Tuples containing variable and corresponding coefficient. Optional. +- **`constant`** (`int|float`) — New Objective constant for the problem. Optional. +- **`sense`** (`enum :py:class:`sense``) — Sets the objective sense to either maximize or minimize. Optional. + +**Examples** + +```python +>>> problem = problem.Problem("MIP_model") +>>> x = problem.addVariable(lb=0.0, vtype=INTEGER) +>>> y = problem.addVariable(lb=0.0, vtype=INTEGER) +>>> problem.setObjective(4*x + y + 4, MAXIMIZE) +>>> problem.updateObjective(coeffs=[(x1, 1.0), (x2, 3.0)], constant=5, + sense=MINIMIZE) +``` + + +#### def `getIncumbentValues` + +```python +getIncumbentValues(solution, vars) +``` + +This is a utility function that can be used for extracting incumbent values of the given variables during a Solve using the incumbent callback. Please check docs for more details and examples of incumbent callbacks. + +**Parameters** + +- **`solution`** (`List[float]`) — Array-like structure containing incumbent values. +- **`vars`** (`List[:py:class:`Variable`]`) — List of variables to extract corresponding incumbent values. + + +#### def `get_incumbent_values` + +```python +get_incumbent_values(solution, vars) +``` + +#### def `getWarmstartData` + +```python +getWarmstartData() +``` + +Note: Applicable to only LP. Allows to retrieve the warm start data from the PDLP solver once the problem is solved. This data can be used to warmstart the next PDLP solve by setting it in :py:meth:`cuopt.linear_programming.solver_settings.SolverSettings.set_pdlp_warm_start_data` # noqa + +**Examples** + +```python +>>> problem = problem.Problem.readMPS("LP.mps") +>>> problem.solve() +>>> warmstart_data = problem.getWarmstartData() +>>> settings.set_pdlp_warm_start_data(warmstart_data) +>>> updated_problem = problem.Problem.readMPS("updated_LP.mps") +>>> updated_problem.solve(settings) +``` + + +#### def `get_pdlp_warm_start_data` + +```python +get_pdlp_warm_start_data() +``` + +#### def `getObjective` + +```python +getObjective() +``` + +Get the Objective expression of the problem. + + +#### def `getVariables` + +```python +getVariables() +``` + +Get a list of all the variables in the problem. + + +#### def `getVariable` + +```python +getVariable(identifier) +``` + +Get a Variable by its index or name. + + +#### def `getConstraints` + +```python +getConstraints() +``` + +Get a list of all the Constraints in a problem. + + +#### def `getConstraint` + +```python +getConstraint(identifier) +``` + +Get a Constraint by its index or name. + + +#### def `read` + +```python +read(cls, file_path, fixed_mps_format = False) +``` + +Initialize a problem from an MPS, QPS, or LP file. + +Dispatches on the file extension via the C++ ``read`` entry point (case-insensitive): ``.mps`` / ``.qps`` (and ``.gz`` / ``.bz2`` variants) use the MPS/QPS reader; ``.lp`` (and compressed variants) use the LP reader. + +**Parameters** + +- **`file_path`** (`str`) — Path to an MPS, QPS, or LP file. +- **`fixed_mps_format`** (`bool`) — If the MPS/QPS reader should parse as fixed MPS format. Ignored for LP inputs. False by default. + +**Returns** + +- **``** (`Problem`) — A problem populated from the file. + +**Examples** + +```python +>>> problem = problem.Problem.read("model.mps") +>>> lp_problem = problem.Problem.read("model.lp") +``` + + +#### def `readMPS` + +```python +readMPS(cls, mps_file) +``` + +Initialize a problem from an `MPS `__ file. # noqa + +Always invokes the MPS/QPS reader directly (via the ``call_parse_mps`` Cython bridge), bypassing extension-based dispatch. Compressed ``.mps.gz`` / ``.mps.bz2`` / ``.qps.gz`` / ``.qps.bz2`` inputs are still supported via the reader's path- based decompression. .. deprecated:: Use :meth:`read` instead. + +**Examples** + +```python +>>> problem = problem.Problem.readMPS("model.mps") +``` + + +#### def `writeMPS` + +```python +writeMPS(mps_file) +``` + +Write the problem into an `MPS `__ file. # noqa Examples -------- >>> problem.writeMPS("model.mps") + + +#### def `NumVariables` + +```python +NumVariables() +``` + +#### def `NumConstraints` + +```python +NumConstraints() +``` + +#### def `getQuadraticConstraints` + +```python +getQuadraticConstraints() +``` + +Returns all quadratic (QCMATRIX) constraints in the problem. + + +#### def `NumNZs` + +```python +NumNZs() +``` + +#### def `IsMIP` + +```python +IsMIP() +``` + +#### def `Obj` + +```python +Obj() +``` + +#### def `ObjValue` + +```python +ObjValue() +``` + +#### def `getCSR` + +```python +getCSR() +``` + +Computes and returns the CSR representation of the constraint matrix. + + +#### def `getQCSR` + +```python +getQCSR() +``` + +Computes and returns the CSR matrix representation of the quadratic objective. + + +#### def `getQcsr` + +```python +getQcsr() +``` + +#### def `relax` + +```python +relax() +``` + +Relax a MIP problem into an LP problem and return the relaxed model. The relaxed model has all variable types set to CONTINUOUS. + +**Examples** + +```python +>>> mip_problem = problem.Problem.readMPS("MIP.mps") +>>> lp_problem = problem.relax() +``` + + +#### def `populate_solution` + +```python +populate_solution(solution) +``` + +#### def `solve` + +```python +solve(settings = solver_settings.SolverSettings()) +``` + +Optimizes the LP or MIP problem with the added variables, constraints and objective. + +**Examples** + +```python +>>> problem = problem.Problem("MIP_model") +>>> x = problem.addVariable(lb=-2.0, ub=8.0, vtype=INTEGER) +>>> y = problem.addVariable(name="Var2") +>>> problem.addConstraint(2*x - 3*y <= 10, name="Constr1") +>>> expr = 3*x + y +>>> problem.addConstraint(expr + x == 20, name="Constr2") +>>> problem.setObjective(x + y, sense=MAXIMIZE) +>>> problem.solve() +``` + + + +## Solver Settings + +
+ +### class `SolverMethod` + +Enum representing different methods to use for solving linear programs. + + +**Members:** `Concurrent`, `PDLP`, `DualSimplex`, `Barrier`, `Unset` + +
+ +### class `PDLPSolverMode` + +Enum representing different solver modes to use in the `SolverSettings.set_pdlp_solver_mode` function. + +**Attributes** + +- **`Stable3`** — Best overall mode from experiments; balances speed and convergence success. If you want to use the legacy version, use Stable1. +- **`Methodical1`** — Takes slower individual steps, but fewer are needed to converge. +- **`Fast1`** — Fastest mode, but with less success in convergence. + +**Notes** + +Default mode is Stable3. + + +**Members:** `Stable1`, `Stable2`, `Methodical1`, `Fast1`, `Stable3` + +
+ +### def `get_solver_setting` + +```python +def get_solver_setting(name) """Return the default string form of solver parameter *name* from a fresh C++ settings object.""" cdef unique_ptr[solver_settings_t[int, double]] unique_solver_settings unique_solver_settings.reset(new solver_settings_t[int, double]()) cdef solver_settings_t[int, double]* c_solver_settings = ( unique_solver_settings.get() ) return c_solver_settings.get_parameter_as_string( name.encode('utf-8') ).decode('utf-8') cpdef get_solver_parameter_names() +``` + +Return the default string form of solver parameter *name* from a fresh C++ settings object. + + + +## Parser + +
+ +### def `Read` + +```python +Read(file_path: str, fixed_mps_format: bool = False) -> DataModel +``` + +Read an optimization problem from a file, dispatching on extension. + +Dispatches to the MPS/QPS or LP reader based on the filename suffix (case-insensitive), matching the C++ ``read`` entry point: - ``.mps``, ``.mps.gz``, ``.mps.bz2``, ``.qps``, ``.qps.gz``, ``.qps.bz2`` → MPS/QPS reader - ``.lp``, ``.lp.gz``, ``.lp.bz2`` → LP reader + +**Parameters** + +- **`file_path`** (`str`) — Path to an MPS, QPS, or LP file (optionally ``.gz`` / ``.bz2`` compressed). +- **`fixed_mps_format`** (`bool`) — If the MPS/QPS reader should parse as fixed MPS format. Ignored for LP inputs. False by default. + +**Returns** + +- **`data_model`** (`DataModel`) — A fully formed LP/MILP/QP problem. + +**Raises** + +- **``** — Parser errors from the underlying C++ readers (via ``catch_io_exception``). +- **``** — If the file extension is not one of the supported suffixes (raised by the C++ ``read`` dispatch). + + +
+ +### def `ParseMps` + +```python +ParseMps(mps_file_path: str, fixed_mps_format: bool = False) -> DataModel +``` + +Read an MPS or QPS file directly via the MPS/QPS reader. + +Unlike :func:`Read`, this function bypasses extension-based dispatch and always invokes the MPS/QPS reader (``read_mps`` on the C++ side), regardless of the filename suffix. Compressed inputs (``.mps.gz``, ``.mps.bz2``, ``.qps.gz``, ``.qps.bz2``) are still supported when zlib / libbz2 are available, because compression is detected from the file path inside the reader. + +**Parameters** + +- **`mps_file_path`** (`str`) — Path to an MPS or QPS file (optionally ``.gz`` / ``.bz2`` compressed). +- **`fixed_mps_format`** (`bool`) — If the MPS/QPS reader should parse the file as fixed MPS format. False by default. + +**Returns** + +- **`data_model`** (`DataModel`) — A fully formed LP/MILP/QP problem. + +**Raises** + +- **``** — Parser errors from the underlying C++ reader (via ``catch_io_exception``). + + + +## Data Model + +
+ +### class `DataModel` + +Initialize a DataModel which represents a Linear Program. + +A linear programming optimization problem is defined as follows: Minimize : dot(c, x) Subject to : matmul(A, x) (= or >= or)<= b Where : x : Decision Variables c : Objective Coefficients A : Constraint Matrix b : Constraint Bounds With : n = number of variables m = number of constraints x = n-dim vector c = n-dim vector A = mxn-dim sparse matrix b = m-dim vector + +**Notes** + +By default, this assumes objective minimization. +To solve a maximization problem, see set_maximization() + +Objective value can be scaled and offseted accordingly: +objective_scaling_factor * (dot(c, x) + objective_offset) +please refer to to the `set_objective_scaling_factor()` +and `set_objective_offset()` method. + +**Examples** + +```python +Minimize: + + cost = 0.2 * VAR1 + 0.1 * VAR2 + +Subject to + + 3 * VAR1 + 4 * VAR2 <= 5.4 + + 2.7 * VAR1 + 10.1 * VAR2 <= 4.9 + + 0 <= VAR1 <= 2 + + 0 <= VAR2 <= inf + +>>> from cuopt import linear_programming +>>> +>>> import numpy as np +>>> +>>> data_model = linear_programming.DataModel() +>>> +>>> # Set the CSR matrix representation, for more information about CSR +>>> # checkout: +>>> # https://docs.nvidia.com/cuda/cusparse/index.html +#compressed-sparse-row-csr +>>> +>>> # Define the different np.array for the CSR representation +>>> # The 4 values of the constraint matrix (A) +>>> A_values = np.array([3.0, 4.0, 2.7, 10.1], dtype=np.float64) +>>> +>>> # The CSR index vector +>>> # Here we associate each value in the A_values to its variable index +>>> # First value correspond to the first variable +>>> # (3.0 -> variable[*0*], constraint[0]) +>>> # Second value correspond to the second variable +>>> # (4.0 -> variable[*1*], constraint[0]) +>>> # Third value correspond to the first variable +>>> # (2.7 -> variable[*0*], constraint[1]) +>>> # Fourth value correspond to the second variable +>>> # (10.1 -> variable[*1*], constraint[1]) +>>> A_indices = np.array([0, 1, 0, 1], dtype=np.int32) +>>> +>>> # The CSR offset vector +>>> # Here we specify the range of values for each constraint +>>> # [0, 2) corresponds to the range of values for the first constraints, +>>> # here [0:3.0, 1:4.0] +>>> # [2, 4) corresponds to the range of values for the second constraint, +>>> # here [1:2.7, 2:10.1] +>>> A_offsets = np.array([0, 2, 4], dtype=np.int32) +>>> +>>> data_model.set_csr_constraint_matrix(A_values, A_indices, A_offsets) +>>> +>>> # Set the constraint bounds (b / right-hand side) array +>>> b = np.array([5.4, 4.9], dtype=np.float64) +>>> data_model.set_constraint_bounds(b) +>>> +>>> # Set the objective coefficient (c) array. +>>> c = np.array([0.2, 0.1], dtype=np.float64) +>>> data_model.set_objective_coefficients(c) +>>> +>>> # Set the constraints/rows equalities, either using the row_type format +>>> # or by directly setting the bounds +>>> +>>> # Method 0: constraints/rows type +>>> # Set both constraints/rows to less-than (<=) +>>> row_types = np.array(['L', 'L']) +>>> data_model.set_row_types(row_types) +>>> +>>> # Method 1: directly set bounds +>>> # Set lower bounds to -infinity and upper bounds to b +>>> constraint_lower_bounds = np.array([-np.inf, -np.inf], +>>> dtype=np.float64) +>>> constraint_upper_bounds = np.array(b, dtype=np.float64) +>>> data_model.set_constraint_lower_bounds(constraint_lower_bounds) +>>> data_model.set_constraint_upper_bounds(constraint_upper_bounds) +>>> +>>> +>>> # Set variable lower and upper bounds +>>> variable_lower_bounds = np.array([0.0, 0.0], dtype=np.float64) +>>> variable_upper_bounds = np.array([2.0, np.inf], dtype=np.float64) +>>> data_model.set_variable_lower_bounds(variable_lower_bounds) +>>> data_model.set_variable_upper_bounds(variable_upper_bounds) +``` + + +#### def `__init__` + +```python +__init__() +``` + +#### def `set_maximize` + +```python +set_maximize(maximize) +``` + +Set the sense of optimization to maximize. + +**Parameters** + +- **`maximize`** (`bool`) — True means to maximize the objective function, else minimize. + +**Notes** + +Setting before calling the solver is optional, default value if false +(minimize). + + +#### def `set_csr_constraint_matrix` + +```python +set_csr_constraint_matrix(A_values, A_indices, A_offsets) +``` + +Set the constraint matrix (A) in CSR format. For more information about CSR checkout: https://docs.nvidia.com/cuda/cusparse/index.html#compressed-sparse-row-csr #noqa + +**Parameters** + +- **`A_values`** (`np.array dtype - float64`) — Values of the CSR representation of the constraint matrix as a device floating point array. +- **`A_indices`** (`np.array dtype - int32`) — Indices of the CSR representation of the constraint matrix as a device integer array. +- **`A_offsets`** (`np.array dtype - int32`) — Offsets of the CSR representation of the constraint matrix as a device integer array. + +**Notes** + +Setting before calling the solver is mandatory. + + +#### def `set_constraint_bounds` + +```python +set_constraint_bounds(b) +``` + +Set the constraint bounds (b / right-hand side) array. + +**Parameters** + +- **`b`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is mandatory. + + +#### def `set_objective_coefficients` + +```python +set_objective_coefficients(c) +``` + +Set the objective coefficients (c) array. + +**Parameters** + +- **`c`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is mandatory. + + +#### def `set_objective_scaling_factor` + +```python +set_objective_scaling_factor(objective_scaling_factor) +``` + +Set the scaling factor of the objective function (scaling_factor * objective_value). + +**Parameters** + +- **`objective_scaling_factor`** (`float64`) — The scaling factor to apply. + +**Notes** + +Setting before calling the solver is optional. + + +#### def `set_objective_offset` + +```python +set_objective_offset(objective_offset) +``` + +Set the offset of the objective function (objective_offset + objective_value). + +**Parameters** + +- **`objective_offset`** (`float64`) — The constant objective_offset to add. + +**Notes** + +Setting before calling the solver is optional. + + +#### def `set_quadratic_objective_matrix` + +```python +set_quadratic_objective_matrix(Q_values, Q_indices, Q_offsets) +``` + +Set the quadratic objective matrix (Q) in CSR format. For more information about CSR checkout: https://docs.nvidia.com/cuda/cusparse/index.html#compressed-sparse-row-csr #noqa + +**Parameters** + +- **`Q_values`** (`np.array dtype - float64`) — Values of the CSR representation of the quadratic objective matrix as a device floating point array. +- **`Q_indices`** (`np.array dtype - int32`) — Indices of the CSR representation of the quadratic objective matrix as a device integer array. +- **`Q_offsets`** (`np.array dtype - int32`) — Offsets of the CSR representation of the quadratic objective matrix as a device integer array. (size: n_variables + 1) + +**Notes** + +Setting before calling the solver is required for quadratic problems. +The input Q matrix is internally symmetrized as Q + Q^T. For convex +quadratic programming, Q + Q^T should be positive semi-definite. + + +#### def `get_quadratic_constraints` + +```python +get_quadratic_constraints() +``` + +Return quadratic constraints appended to this model. + +Each entry is a dict with keys including ``constraint_row_index``, ``constraint_row_name``, ``constraint_row_type``, COO arrays, and ``rhs_value``. + + +#### def `clear_quadratic_constraints` + +```python +clear_quadratic_constraints() +``` + +Remove all quadratic constraints from the model. + + +#### def `add_quadratic_constraint` + +```python +add_quadratic_constraint(constraint_row_name = '', linear_values = None, linear_indices = None, rhs_value = 0.0, vals = None, rows = None, cols = None, sense = 'L') +``` + +Add a quadratic constraint. + +Each constraint has a linear part (optional) and a quadratic part in COO format. Call multiple times to add several quadratic constraints. + +**Parameters** + +- **`constraint_row_name`** (`str, optional`) — Optional row name. +- **`linear_values, linear_indices`** (`array-like, optional`) — Sparse linear coefficients on the same variable index space. +- **`rhs_value`** (`float, optional`) — Right-hand side of the constraint. +- **`vals, rows, cols`** (`array-like`) — COO triplets for the quadratic matrix Q in ``linear^T x + x^T Q x {sense} rhs_value``. +- **`sense`** (`str, optional`) — Constraint sense: ``'L'`` (default, ``<=``) or ``'G'`` (``>=``). ``'G'`` constraints are converted to ``'L'`` internally. Equality (``'E'``) is not supported. + +**Notes** + +When any quadratic constraint is present, cuOpt selects the barrier +solver and converts quadratic constraints to second-order cones. + + +#### def `set_variable_lower_bounds` + +```python +set_variable_lower_bounds(variable_lower_bounds) +``` + +Set the variables (x) lower bounds. + +**Parameters** + +- **`variable_lower_bounds`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional, default value for all is +0. + + +#### def `set_variable_upper_bounds` + +```python +set_variable_upper_bounds(variable_upper_bounds) +``` + +Set the variables (x) upper bounds. + +**Parameters** + +- **`variable_upper_bounds`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional, default value for all is ++infinity. + + +#### def `set_constraint_lower_bounds` + +```python +set_constraint_lower_bounds(constraint_lower_bounds) +``` + +Set the constraints lower bounds. + +**Parameters** + +- **`constraint_lower_bounds`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional if you set the row type, +else it's mandatory along with the upper bounds. + + +#### def `set_constraint_upper_bounds` + +```python +set_constraint_upper_bounds(constraint_upper_bounds) +``` + +Set the constraints upper bounds. + +**Parameters** + +- **`constraint_upper_bounds`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional if you set the row type, +else it's mandatory along with the lower bounds. + + +#### def `set_variable_types` + +```python +set_variable_types(variable_types) +``` + +Set the variable types. + +**Parameters** + +- **`variables_types`** (`np.array dtype - unicode string (= ): lower constrains bound equal to b, upper constrains bound equal to +infinity + +**Parameters** + +- **`row_types`** (`np.array dtype - unicode string (>> row_types = np.array(['L', 'L']) +>>> data_model.set_row_types(row_types) +``` + + +#### def `set_variable_names` + +```python +set_variable_names(variables_names) +``` + +Set the variables names. + +**Parameters** + +- **`variables_names`** (`np.array dtype - unicode string`) — Host string array. + +**Notes** + +Setting before calling the solver is optional. Value is only used for +file generation of the solution. + + +#### def `set_row_names` + +```python +set_row_names(row_names) +``` + +Set the row names. + +**Parameters** + +- **`row_names`** (`np.array dtype - unicode string`) — Host string array. + +**Notes** + +Setting before calling the solver is optional. Value is only used for +file generation of the solution. + + +#### def `set_objective_name` + +```python +set_objective_name(objective_name) +``` + +Set the objective name as string. + + +#### def `set_problem_name` + +```python +set_problem_name(problem_name) +``` + +Set the problem name as string. + + +#### def `set_initial_primal_solution` + +```python +set_initial_primal_solution(initial_primal_solution) +``` + +Set the initial primal solution. + +**Parameters** + +- **`initial_primal_solution`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional. + + +#### def `set_initial_dual_solution` + +```python +set_initial_dual_solution(initial_dual_solution) +``` + +NOTE: Not supported for MILP. + +Set the initial dual solution. + +**Parameters** + +- **`initial_dual_solution`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional. + + +#### def `get_sense` + +```python +get_sense() +``` + +Get the sense of optimization as a bool. True means maximize the objective function, false means minimize. + + +#### def `get_constraint_matrix_values` + +```python +get_constraint_matrix_values() +``` + +Get the values of the CSR representation of the constraint matrix as numpy.array with float64 type. + + +#### def `get_constraint_matrix_indices` + +```python +get_constraint_matrix_indices() +``` + +Get the indices of the CSR representation of the constraint matrix as numpy.array with int type. + + +#### def `get_constraint_matrix_offsets` + +```python +get_constraint_matrix_offsets() +``` + +Get the indices of the CSR representation of the constraint matrix as numpy.array with int type. + + +#### def `get_constraint_bounds` + +```python +get_constraint_bounds() +``` + +Get the constraint bounds (b / right-hand side) as numpy.array with float64 type. + + +#### def `get_objective_coefficients` + +```python +get_objective_coefficients() +``` + +Get the objective_coefficients (c) as numpy.array with float64 type. + + +#### def `get_objective_scaling_factor` + +```python +get_objective_scaling_factor() +``` + +Get the scaling factor of the objective function as a float64. + + +#### def `get_objective_offset` + +```python +get_objective_offset() +``` + +Get the offset of the objective function as a float64. + + +#### def `get_quadratic_objective_values` + +```python +get_quadratic_objective_values() +``` + +Get the values of the CSR representation of the quadratic objective matrix as a numpy.array with float64 type. + + +#### def `get_quadratic_objective_indices` + +```python +get_quadratic_objective_indices() +``` + +Get the indices of the CSR representation of the quadratic objective matrix as a numpy.array with int32 type. + + +#### def `get_quadratic_objective_offsets` + +```python +get_quadratic_objective_offsets() +``` + +Get the offsets of the CSR representation of the quadratic objective matrix as a numpy.array with int32 type. + + +#### def `get_variable_lower_bounds` + +```python +get_variable_lower_bounds() +``` + +Get the variables (x) lower bounds as numpy.array with float64 type. + + +#### def `get_variable_upper_bounds` + +```python +get_variable_upper_bounds() +``` + +Get the variables (x) upper bounds as numpy.array with float64 type. + + +#### def `get_constraint_lower_bounds` + +```python +get_constraint_lower_bounds() +``` + +Get the constraints lower bounds as numpy.array with float64 type. + + +#### def `get_constraint_upper_bounds` + +```python +get_constraint_upper_bounds() +``` + +Get the constraints upper bounds as numpy.array with float64 type. + + +#### def `get_row_types` + +```python +get_row_types() +``` + +Get the type of each row (constraint) as numpy.array with char8 type. + + +#### def `get_ascii_row_types` + +```python +get_ascii_row_types() +``` + +Get the type of each row (constraint) converted to a numpy.array with int8 type (ascii value). + + +#### def `get_initial_primal_solution` + +```python +get_initial_primal_solution() +``` + +Get the initial primal solution as numpy.array with float64 type. + + +#### def `get_initial_dual_solution` + +```python +get_initial_dual_solution() +``` + +NOTE: Not supported for MILP. + +Get the initial dual solution as numpy.array with float64 type. + + +#### def `get_variable_types` + +```python +get_variable_types() +``` + +Get the variable types as numpy.array with char type. + + +#### def `get_variable_names` + +```python +get_variable_names() +``` + +Get the variable names as numpy.array with string type. + + +#### def `get_row_names` + +```python +get_row_names() +``` + +Get the row names as numpy.array with string type. + + +#### def `get_objective_name` + +```python +get_objective_name() +``` + +Get the objective name as string. + + +#### def `get_problem_name` + +```python +get_problem_name() +``` + +Get the problem name as string. + + +#### def `writeMPS` + +```python +writeMPS(user_problem_file) +``` + + +## Solution + +
+ +### class `PDLPWarmStartData` + +#### def `__init__` + +```python +__init__(current_primal_solution, current_dual_solution, initial_primal_average, initial_dual_average, current_ATY, sum_primal_solutions, sum_dual_solutions, last_restart_duality_gap_primal_solution, last_restart_duality_gap_dual_solution, initial_primal_weight, initial_step_size, total_pdlp_iterations, total_pdhg_iterations, last_candidate_kkt_score, last_restart_kkt_score, sum_solution_weight, iterations_since_last_restart) +``` + +
+ +### class `Solution` + +A container of LP solver output + +**Parameters** + +- **`problem_category`** (`int`) — Whether it is a LP-0, MIP-1 or IP-2 solution +- **`vars`** (`Dict[str, float64]`) — Dictionary mapping each variable (name) to its value. +- **`primal_solution`** (`numpy.array`) — Primal solution of the LP problem +- **`dual_solution`** (`numpy.array`) — Note: Applicable to only LP Dual solution of the LP problem +- **`reduced_cost`** (`numpy.array`) — Note: Applicable to only LP The reduced cost. It contains the dual multipliers for the linear constraints. +- **`termination_status: Integer`** — Termination status value. +- **`primal_residual: Float64`** — L2 norm of the primal residual: measurement of the primal infeasibility +- **`dual_residual: Float64`** — Note: Applicable to only LP L2 norm of the dual residual: measurement of the dual infeasibility +- **`primal_objective: Float64`** — Value of the primal objective +- **`dual_objective: Float64`** — Note: Applicable to only LP Value of the dual objective +- **`gap: Float64`** — Difference between the primal and dual objective +- **`nb_iterations: Int`** — Number of iterations the LP solver did before converging +- **`mip_gap: float64`** — Note: Applicable to only MILP The relative difference between the best integer objective value found so far and the objective bound. A value of 0.01 means the solution is guaranteed to be within 1% of optimal. +- **`solution_bound: float64`** — Note: Applicable to only MILP The best known bound on the optimal objective value. For minimization problems, this is a lower bound on the optimal value. For maximization problems, this is an upper bound. +- **`max_constraint_violation: float64`** — Note: Applicable to only MILP The maximum amount by which any constraint is violated in the current solution. Should be close to zero for a feasible solution. +- **`max_int_violation: float64`** — Note: Applicable to only MILP The maximum amount by which any integer variable deviates from being an integer. A value of 0 means all integer variables have integral values. +- **`max_variable_bound_violation: float64`** — Note: Applicable to only MILP The maximum amount by which any variable violates its upper or lower bounds in the current solution. Should be zero for a feasible solution. +- **`presolve_time: float64`** — Note: Applicable to only MILP Time used for pre-solve +- **`solve_time: Float64`** — Solve time in seconds +- **`solved_by: enum`** — Note: Applicable to only LP Whether the LP was solved by Dual Simplex, PDLP or Barrier. This is populated by the solver using the values from SolverMethod. + + +#### def `__init__` + +```python +__init__(problem_category, vars, solve_time = 0.0, primal_solution = None, dual_solution = None, reduced_cost = None, current_primal_solution = None, current_dual_solution = None, initial_primal_average = None, initial_dual_average = None, current_ATY = None, sum_primal_solutions = None, sum_dual_solutions = None, last_restart_duality_gap_primal_solution = None, last_restart_duality_gap_dual_solution = None, initial_primal_weight = 0.0, initial_step_size = 0.0, total_pdlp_iterations = 0, total_pdhg_iterations = 0, last_candidate_kkt_score = 0.0, last_restart_kkt_score = 0.0, sum_solution_weight = 0.0, iterations_since_last_restart = 0, termination_status = 0, error_status = 0, error_message = '', primal_residual = 0.0, dual_residual = 0.0, primal_objective = 0.0, dual_objective = 0.0, gap = 0.0, nb_iterations = 0, solved_by = SolverMethod.Unset, mip_gap = 0.0, solution_bound = 0.0, presolve_time = 0.0, max_constraint_violation = 0.0, max_int_violation = 0.0, max_variable_bound_violation = 0.0, num_nodes = 0, num_simplex_iterations = 0) +``` + +#### def `raise_if_milp_solution` + +```python +raise_if_milp_solution(function_name) +``` + +#### def `raise_if_lp_solution` + +```python +raise_if_lp_solution(function_name) +``` + +#### def `get_primal_solution` + +```python +get_primal_solution() +``` + +Returns the primal solution as numpy.array with float64 type. + + +#### def `get_dual_solution` + +```python +get_dual_solution() +``` + +Note: Applicable to only LP Returns the dual solution as numpy.array with float64 type. + + +#### def `get_primal_objective` + +```python +get_primal_objective() +``` + +Returns the primal objective as a float64. + + +#### def `get_dual_objective` + +```python +get_dual_objective() +``` + +Note: Applicable to only LP Returns the dual objective as a float64. + + +#### def `get_termination_status` + +```python +get_termination_status() +``` + +Returns the termination status as per TerminationReason. + + +#### def `get_termination_reason` + +```python +get_termination_reason() +``` + +Returns the termination reason as per TerminationReason. + + +#### def `get_error_status` + +```python +get_error_status() +``` + +Returns the error status as per ErrorStatus. + + +#### def `get_error_message` + +```python +get_error_message() +``` + +Returns the error message as per ErrorMessage. + + +#### def `get_solve_time` + +```python +get_solve_time() +``` + +Returns the engine solve time in seconds as a float64. + + +#### def `get_solved_by_pdlp` + +```python +get_solved_by_pdlp() +``` + +#### def `get_solved_by` + +```python +get_solved_by() +``` + +Returns whether the LP was solved by Dual Simplex, PDLP or Barrier. See SolverMethod for all possible values. + + +#### def `get_vars` + +```python +get_vars() +``` + +Returns the dictionnary mapping each variable (name) to its value. + + +#### def `get_lp_stats` + +```python +get_lp_stats() +``` + +Note: Applicable to only LP Returns the convergence statistics as a dictionary: + +"primal_residual": float64 Measurement of the primal infeasibility. This quantity is being reduced until primal tolerance is met (see SolverSettings primal_tolerance). "dual_residual": float64, Measurement of the dual infeasibility. This quantity is being reduced until dual tolerance is met (see SolverSettings dual_tolerance). "gap": float64 Difference between the primal and dual objective. This quantity is being reduced until gap tolerance is met (see SolverSettings gap_tolerance). - "nb_iterations": int Number of iterations the LP solver did before converging. + + +#### def `get_reduced_cost` + +```python +get_reduced_cost() +``` + +Note: Applicable to only LP Returns the reduced cost as numpy.array with float64 type. + + +#### def `get_pdlp_warm_start_data` + +```python +get_pdlp_warm_start_data() +``` + +Note: Applicable to only LP + +Allows to retrieve the warm start data from the PDLP solver. See `SolverSettings.set_pdlp_warm_start_data` for more details. + + +#### def `get_milp_stats` + +```python +get_milp_stats() +``` + +Note: Applicable to only MILP Returns the convergence statistics as a dictionary: + +mip_gap: float64 The relative difference between the best integer objective value found so far and the objective bound. A value of 0.01 means the solution is guaranteed to be within 1% of optimal. presolve_time: float64 Time took for pre-solve max_constraint_violation: float64 The maximum amount by which any constraint is violated in the current solution. Should be close to zero for a feasible solution . max_int_violation: float64 The maximum amount by which any integer variable deviates from being an integer. A value of 0 means all integer variables have integral values. max_variable_bound_violation: float64 The maximum amount by which any variable violates its upper or lower bounds in the current solution. Should be zero for a feasible solution. solution_bound: float64 The best known bound on the optimal objective value. For minimization problems, this is a lower bound on the optimal value. For maximization problems, this is an upper bound. num_nodes: int Number of nodes explored during the MIP solve num_simplex_iterations: int Number of simplex iterations performed during the MIP solve + + +#### def `get_problem_category` + +```python +get_problem_category() +``` + +Returns one of the problem category from ProblemCategory + +LP - 0 MIP - 1 IP - 2 + + + +## Solver + +
+ +### def `Solve` + +```python +Solve(data_model, solver_settings = None) +``` + +Solve the Linear Program passed as input and returns the solution. + +Data Model object can be construed through setters (see linear_programming.DataModel class) or through a MPS file (see cuopt.linear_programming.Read function) + +**Parameters** + +- **`data_model`** (`DataModel`) — Data model containing a representation of a linear program on standard form. +- **`solver_settings: SolverSettings`** — Settings to configure solver configurations. By default, it uses default solver settings to solve. + +**Returns** + +- **``** (`solution: Solution`) — Solution object containing both primal and dual solutions, objectives and more statistics about the result. + +**Notes** + +Both primal and dual solutions are zero-initialized. +For custom initialization, please refer to `set_initial_primal_solution()` +and `set_initial_dual_solution()` methods. +For more details on the Solution object, see linear_programming.Solution. + +**Examples** + +```python +>>> from cuopt import linear_programming +>>> from cuopt.linear_programming.solver_settings import PDLPSolverMode +>>> from cuopt.linear_programming.solver.solver_parameters import * +>>> +>>> data_model = linear_programming.DataModel() +>>> +>>> # Set all required fields +>>> data_model.data_model.set_csr_constraint_matrix(...) +>>> +>>> # Build a solver setting object +>>> settings = linear_programming.SolverSettings() +>>> # Lower the accuracy from 1e-4 to 1e-2 for a faster result +>>> settings.set_optimality_tolerance(1e-2) +>>> # Also change the solver mode to Fast1 which can be faster +>>> settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Fast1) +>>> +>>> # Call solve +>>> solution = linear_programming.Solve(data_model, settings) +>>> +>>> # Print solution +>>> print(solution.get_primal_solution()) +>>> # Print the primal objective +>>> print(solution.get_primal_objective()) +>>> # Print the value of one specific variable +>>> print(solution.get_vars()["var_name"]) +``` + + +
+ +### def `BatchSolve` + +```python +BatchSolve(data_model_list, solver_settings = None) +``` + +Solve the list of Linear Programs passed as input and returns the solutions and total solve time. + +.. deprecated:: LP BatchSolve is deprecated and will be removed in a future release. It runs concurrent LPs in multiple C++ threads, which can be done independently in user code. Use sequential :func:`Solve` calls instead, e.g. ``[Solve(dm, solver_settings) for dm in data_model_list]``, or implement your own parallelism (e.g. ``concurrent.futures``). Data Model objects can be construed through setters (see linear_programming.DataModel class) or through a MPS file (see cuopt.linear_programming.Read function) + +**Parameters** + +- **`data_models`** (`list of DataModel`) — Data models containing a representation of a linear program on standard form. +- **`solver_settings: SolverSettings`** — Settings to configure solver configurations. By default, it uses default solver settings to solve. + +**Returns** + +- **``** (`solution: list of Solution`) — List of Solution objects containing both primal and dual solutions, objectives and more statistics about the result. +- **``** (`solve_time: double`) — The engine solve time for the whole batch in seconds as a float64. + +**Notes** + +Solving in batch is usually faster than solving one by one. +The total time to solve the whole batch on the engine side is returned as +summing up the solutions `get_solve_time` would be incorrect as they are +solved together in parallel, overlapping multiple solve. +Both primal and dual solutions are zero-initialized. +For custom initialization, please refer to `set_initial_primal_solution()` +and `set_initial_dual_solution()` methods. +For more details on the Solution object, see linear_programming.Solution. + +**Examples** + +```python +>>> from cuopt import linear_programming +>>> from cuopt.linear_programming.solver_settings import PDLPSolverMode +>>> from cuopt.linear_programming.solver.solver_parameters import * +>>> from cuopt.linear_programming import Read +>>> +>>> data_models = [] +>>> for i in range(...): +>>> data_models.append(Read(...)) +>>> +>>> # Build a solver setting object +>>> settings = linear_programming.SolverSettings() +>>> # Lower the accuracy from 1e-4 to 1e-2 for a faster result +>>> settings.set_optimality_tolerance(1e-2) +>>> # Also change the solver mode to PDLPSolverMode.Fast1 +>>> # which can be faster +>>> settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Fast1) +>>> +>>> # Call solve +>>> solutions, solve_time = linear_programming.BatchSolve( +>>> data_models, +>>> settings +>>> ) +>>> +>>> # Print total engine solve time +>>> print("Total engine solve time: ", solve_time) +>>> # Print solutions +>>> for solution in solutions: +>>> print(solution.get_primal_solution()) +>>> # Print the primal objective +>>> print(solution.get_primal_objective()) +>>> # Print the value of one specific variable +>>> print(solution.get_vars()["var_name"]) +``` diff --git a/fern/docs/pages/cuopt-python/convex/convex-examples.mdx b/fern/docs/pages/cuopt-python/convex/convex-examples.mdx new file mode 100644 index 0000000000..e438f0f04d --- /dev/null +++ b/fern/docs/pages/cuopt-python/convex/convex-examples.mdx @@ -0,0 +1,824 @@ +--- +title: "Convex Optimization Examples" +--- + +This section contains examples of how to use the cuOpt convex optimization Python API. + + +The examples in this section are not exhaustive. They are provided to help you get started with the cuOpt convex optimization Python API. For more examples, please refer to the [cuopt-examples GitHub repository](https://github.com/NVIDIA/cuopt-examples). + + +## Simple Linear Programming Example + +{/* embed: examples/simple_lp_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Simple Linear Programming Example + +This example demonstrates how to: +- Create a linear programming problem +- Add continuous variables +- Add linear constraints +- Set an objective function +- Solve the problem and retrieve results + +Problem: + Maximize: x + y + Subject to: + x + y <= 10 + x - y >= 0 + x, y >= 0 + +Expected Output: + Optimal solution found in 0.01 seconds + x = 10.0 + y = 0.0 + Objective value = 10.0 +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + """Run the simple LP example.""" + # Create a new problem + problem = Problem("Simple LP") + + # Add variables + x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + + # Add constraints + problem.addConstraint(x + y <= 10, name="c1") + problem.addConstraint(x - y >= 0, name="c2") + + # Set objective function + problem.setObjective(x + y, sense=MAXIMIZE) + + # Configure solver settings + settings = SolverSettings() + settings.set_parameter("time_limit", 60) + + # Solve the problem + problem.solve(settings) + + # Check solution status + if problem.Status.name == "Optimal": + print(f"Optimal solution found in {problem.SolveTime:.2f} seconds") + print(f"x = {x.getValue()}") + print(f"y = {y.getValue()}") + print(f"Objective value = {problem.ObjValue}") + else: + print(f"Problem status: {problem.Status.name}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: + +## Simple Quadratic Programming Example + +{/* embed: examples/simple_qp_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Simple Quadratic Programming Example +==================================== + +This example demonstrates how to formulate and solve a simple +Quadratic Programming (QP) problem using the cuOpt Python API. + +Problem: + minimize x^2 + y^2 + subject to x + y >= 1 + 0.75 * x + y <= 1 + x, y >= 0 + +This is a convex QP that minimizes the squared distance from the origin +while satisfying other constraints. +""" + +from cuopt.linear_programming.problem import ( + MINIMIZE, + Problem, +) + + +def main(): + # Create a new optimization problem + prob = Problem("Simple QP") + + # Add variables with non-negative bounds + x = prob.addVariable(lb=0) + y = prob.addVariable(lb=0) + + # Add constraint: x + y >= 1 + prob.addConstraint(x + y >= 1) + prob.addConstraint(0.75 * x + y <= 1) + + # Set quadratic objective: minimize x^2 + y^2 + # Using Variable * Variable to create quadratic terms + quad_obj = x * x + y * y + prob.setObjective(quad_obj, sense=MINIMIZE) + + # Solve the problem + prob.solve() + + # Print results + print(f"Optimal solution found in {prob.SolveTime:.2f} seconds") + print(f"x = {x.Value}") + print(f"y = {y.Value}") + print(f"Objective value = {prob.ObjValue}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: + +## Second-Order Cone Programming Example + +{/* embed: examples/simple_socp_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Simple Second-Order Cone Programming (SOCP) Example +=================================================== + +This example demonstrates how to formulate and solve a Second-Order Cone +Program (SOCP) using the cuOpt Python API. + +Problem: + minimize x3 + subject to x1 + x2 >= 2 + x1^2 + x2^2 <= x3^2 (i.e. ||(x1, x2)||_2 <= x3) + x3 >= 0 + +The cone constraint is supplied as the quadratic inequality +``x1^2 + x2^2 - x3^2 <= 0``. cuOpt detects the second-order cone structure and +solves with the barrier method. + +Optimal solution: x1 = x2 = 1, x3 = sqrt(2) ~= 1.4142, objective ~= 1.4142. +""" + +from cuopt.linear_programming.problem import ( + MINIMIZE, + Problem, +) + + +def main(): + prob = Problem("Simple SOCP") + + # Cone tail variables can be free; the cone head must be non-negative. + x1 = prob.addVariable(lb=-float("inf"), name="x1") + x2 = prob.addVariable(lb=-float("inf"), name="x2") + x3 = prob.addVariable(lb=0, name="x3") + + # Linear constraint + prob.addConstraint(x1 + x2 >= 2) + + # Second-order cone ||(x1, x2)||_2 <= x3 as x1^2 + x2^2 - x3^2 <= 0 + prob.addConstraint(x1 * x1 + x2 * x2 - x3 * x3 <= 0, name="soc") + + prob.setObjective(x3, sense=MINIMIZE) + + # cuOpt automatically selects the barrier method for quadratic constraints. + prob.solve() + + print(f"Status: {prob.Status}") + print(f"x1 = {x1.Value}") + print(f"x2 = {x2.Value}") + print(f"x3 = {x3.Value}") + print(f"Objective value = {prob.ObjValue}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +This example minimizes `x3` subject to `x1 + x2 >= 2` and the second-order cone `||(x1, x2)||_2 <= x3`, expressed as the inequalities `x1^2 + x2^2 - x3^2 <= 0, x_3 >= 0`. cuOpt detects the cone structure and solves with the barrier method. + +The response is as follows: + +## Second-Order Cone Programming with Rotated Second-Order Cones Example + +{/* embed: examples/rotated_socp_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Rotated Second-Order Cone Programming (SOCP) Example +==================================================== + +This example formulates and solves a rotated second-order cone problem with the +cuOpt Python API. + +Problem: + minimize x3 + x4 + subject to x1 + x2 >= 2 + x1^2 + x2^2 <= x3 * x4 (rotated second-order cone) + x3 >= 0, x4 >= 0 + +The rotated cone is written as ``x1^2 + x2^2 - x3*x4 <= 0``. + +Optimal solution: x1 = x2 = 1, x3 = x4 = sqrt(2) ~= 1.4142, objective ~= 2.8284. +""" + +from cuopt.linear_programming.problem import ( + MINIMIZE, + Problem, +) + + +def main(): + prob = Problem("Rotated SOCP") + + # Cone tail variables can be free; the cone heads must be non-negative. + x1 = prob.addVariable(lb=-float("inf"), name="x1") + x2 = prob.addVariable(lb=-float("inf"), name="x2") + x3 = prob.addVariable(lb=0, name="x3") + x4 = prob.addVariable(lb=0, name="x4") + + # Linear constraint + prob.addConstraint(x1 + x2 >= 2) + + # x1^2 + x2^2 <= x3*x4 + prob.addConstraint( + x1 * x1 + x2 * x2 - x3 * x4 <= 0, + name="rotated_soc", + ) + + prob.setObjective(x3 + x4, sense=MINIMIZE) + + # cuOpt automatically selects the barrier method for quadratic constraints. + prob.solve() + + print(f"Status: {prob.Status}") + print(f"x1 = {x1.Value}") + print(f"x2 = {x2.Value}") + print(f"x3 = {x3.Value}") + print(f"x4 = {x4.Value}") + print(f"Objective value = {prob.ObjValue}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +This example solves a **rotated** second-order cone `x1^2 + x2^2 <= x3 * x4, x3 >= 0, x4 >= 0`. The cross term is written as `-x3*x4`. See [Convex Features](convex-features) for other RSOC forms. It minimizes `x3 + x4` subject to `x1 + x2 >= 2`. + +The response is as follows: + +## General Convex Quadratic Constraint Example + +{/* embed: examples/general_quadratic_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +General Convex Quadratic Constraint Example +=========================================== + +This example demonstrates adding a general convex quadratic constraint +with the cuOpt Python API. + +Problem: + minimize x + y + subject to x + y >= -5 + 2*x^2 + 2*x*y + 2*y^2 <= 6 (general convex quadratic) + +The quadratic constraint is an ellipsoid (a convex set). The +linear objective is minimized on it at x = y = -1, objective -2. +""" + +from cuopt.linear_programming.problem import ( + MINIMIZE, + Problem, +) + + +def main(): + prob = Problem("General Convex QC") + + x = prob.addVariable(lb=-float("inf"), name="x") + y = prob.addVariable(lb=-float("inf"), name="y") + + # A linear constraint + prob.addConstraint(x + y >= -5) + + # General convex quadratic constraint 2*x^2 + 2*x*y + 2*y^2 <= 6. + prob.addConstraint( + 2 * x * x + 2 * x * y + 2 * y * y <= 6, name="ellipsoid" + ) + + prob.setObjective(x + y, sense=MINIMIZE) + + # cuOpt automatically selects the barrier method for problems with quadratic constraints. + prob.solve() + + print(f"Status: {prob.Status}") + print(f"x = {x.Value}") + print(f"y = {y.Value}") + print(f"Objective value = {prob.ObjValue}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +This example uses a general convex quadratic constraint `2*x^2 + 2*x*y + 2*y^2 <= 6` (an ellipsoid). It minimizes `x + y`. + +The response is as follows: + +## Reading a Problem from an MPS File + +[mps\_example.py](examples/mps_example.py), [sample.mps](examples/sample.mps) + +`Problem.read` loads a problem from an MPS, QPS, or LP file, dispatching on the file extension. The same call also reads `QUADOBJ` quadratic objectives and and `QCMATRIX` quadratic constraints. This example reads the bundled `sample.mps` (a small LP) and solves it. + +The sample MPS file: + +The response is as follows: + +## Advanced Example: Production Planning + +{/* embed: examples/production_planning_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Production Planning Example + +This example demonstrates a real-world application of MIP: +- Production planning with resource constraints +- Multiple constraint types (machine time, labor, materials) +- Profit maximization + +Problem: + A factory produces two products (A and B) + - Product A: $50 profit per unit + - Product B: $30 profit per unit + + Resources: + - Machine time: 2 hrs/unit A, 1 hr/unit B, max 100 hrs + - Labor: 1 hr/unit A, 3 hrs/unit B, max 120 hrs + - Material: 4 units/unit A, 2 units/unit B, max 200 units + + Constraints: + - Minimum 10 units of Product A + - Minimum 15 units of Product B + +Expected Output: + === Production Planning Solution === + Status: Optimal + Solve time: 0.09 seconds + Product A production: 36.0 units + Product B production: 28.0 units + Total profit: $2640.00 +""" + +from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + """Run the production planning example.""" + # Production planning problem + problem = Problem("Production Planning") + + # Decision variables: production quantities + # x1 = units of product A + # x2 = units of product B + x1 = problem.addVariable(lb=10, vtype=INTEGER, name="Product_A") + x2 = problem.addVariable(lb=15, vtype=INTEGER, name="Product_B") + + # Resource constraints + # Machine time: 2 hours per unit of A, 1 hour per unit of B, max 100 hours + problem.addConstraint(2 * x1 + x2 <= 100, name="Machine_Time") + + # Labor: 1 hour per unit of A, 3 hours per unit of B, max 120 hours + problem.addConstraint(x1 + 3 * x2 <= 120, name="Labor_Hours") + + # Material: 4 units per unit of A, 2 units per unit of B, max 200 units + problem.addConstraint(4 * x1 + 2 * x2 <= 200, name="Material") + + # Objective: maximize profit + # Profit: $50 per unit of A, $30 per unit of B + problem.setObjective(50 * x1 + 30 * x2, sense=MAXIMIZE) + + # Solve with time limit + settings = SolverSettings() + settings.set_parameter("time_limit", 30) + problem.solve(settings) + + # Display results + if problem.Status.name == "Optimal": + print("=== Production Planning Solution ===") + print(f"Status: {problem.Status.name}") + print(f"Solve time: {problem.SolveTime:.2f} seconds") + print(f"Product A production: {x1.getValue()} units") + print(f"Product B production: {x2.getValue()} units") + print(f"Total profit: ${problem.ObjValue:.2f}") + + else: + print(f"Problem not solved optimally. Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: + +## Working with Expressions and Constraints + +{/* embed: examples/expressions_constraints_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Working with Expressions and Constraints Example + +This example demonstrates: +- Creating complex linear expressions +- Using expressions in constraints +- Different constraint types (<=, >=, ==) +- Building constraints from multiple variables + +Problem: + Maximize: x + 2*y + 3*z + Subject to: + 2*x + 3*y - z <= 100 (Complex_Constraint_1) + x + y + z >= 20 (Complex_Constraint_2) + x + y == 50 (Equality_Constraint) + x <= 30 (Upper_Bound_X) + y >= 10 (Lower_Bound_Y) + z <= 100 (Upper_Bound_Z) + +Expected Output: + === Expression Example Results === + x = 0.0 + y = 50.0 + z = 100.0 + Objective value = 400.0 +""" + +from cuopt.linear_programming.problem import Problem, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + """Run the expressions and constraints example.""" + problem = Problem("Expression Example") + + # Create variables + x = problem.addVariable(lb=0, name="x") + y = problem.addVariable(lb=0, name="y") + z = problem.addVariable(lb=0, name="z") + + # Create complex expressions + expr1 = 2 * x + 3 * y - z + expr2 = x + y + z + + # Add constraints using expressions + problem.addConstraint(expr1 <= 100, name="Complex_Constraint_1") + problem.addConstraint(expr2 >= 20, name="Complex_Constraint_2") + + # Add constraint with different senses + problem.addConstraint(x + y == 50, name="Equality_Constraint") + problem.addConstraint(1 * x <= 30, name="Upper_Bound_X") + problem.addConstraint(1 * y >= 10, name="Lower_Bound_Y") + problem.addConstraint(1 * z <= 100, name="Upper_Bound_Z") + + # Set objective + problem.setObjective(x + 2 * y + 3 * z, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter("time_limit", 20) + + problem.solve(settings) + + if problem.Status.name == "Optimal": + print("=== Expression Example Results ===") + print(f"x = {x.getValue()}") + print(f"y = {y.getValue()}") + print(f"z = {z.getValue()}") + print(f"Objective value = {problem.ObjValue}") + else: + print(f"Problem status: {problem.Status.name}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: + +## Working with Quadratic Objective Matrix + +{/* embed: examples/qp_matrix_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 + +""" +Quadratic Programming Matrix Example +==================================== + +This example demonstrates how to formulate and solve a +Quadratic Programming (QP) problem represented in a matrix format +using the cuOpt Python API. + +Problem: + minimize 0.01 * p1^2 + 0.02 * p2^2 + 0.015 * p3^2 + 8 * p1 + 6 * p2 + 7 * p3 + subject to p1 + p2 + p3 = 150 + 10 <= p1 <= 100 + 10 <= p2 <= 80 + 10 <= p3 <= 90 + +This is a convex QP that minimizes the cost of power generation and dispatch +while satisfying capacity and demand. +""" + +from cuopt.linear_programming.problem import ( + MINIMIZE, + Problem, + QuadraticExpression, +) + + +def main(): + # Create a new optimization problem + prob = Problem("QP Power Dispatch") + + # Add variables with lower and upper bounds + p1 = prob.addVariable(lb=10, ub=100) + p2 = prob.addVariable(lb=10, ub=80) + p3 = prob.addVariable(lb=10, ub=90) + + # Add demand constraint: p1 + p2 + p3 = 150 + prob.addConstraint(p1 + p2 + p3 == 150, name="demand") + + # Create matrix for quadratic terms: 0.01 p1^2 + 0.02 p2^2 + 0.015 p3^2 + matrix = [[0.01, 0.0, 0.0], [0.0, 0.02, 0.0], [0.0, 0.0, 0.015]] + quad_matrix = QuadraticExpression(matrix, prob.getVariables()) + + # Set objective using matrix representation + quad_obj = quad_matrix + 8 * p1 + 6 * p2 + 7 * p3 + prob.setObjective(quad_obj, sense=MINIMIZE) + + # Solve the problem + prob.solve() + + # Print results + print(f"Optimal solution found in {prob.SolveTime:.2f} seconds") + print(f"p1 = {p1.Value}") + print(f"p2 = {p2.Value}") + print(f"p3 = {p3.Value}") + print(f"Minimized cost = {prob.ObjValue}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: + +## Inspecting the Problem Solution + +{/* embed: examples/solution_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Linear Programming Solution Example + +This example demonstrates how to: +- Create a linear programming problem +- Solve the problem and retrieve result details + +Problem: + Minimize: 3x + 2y + 5z + Subject to: + x + y + z = 4 + 2x + y + z = 5 + x, y, z >= 0 + +Expected Output: + Optimal solution found in 0.02 seconds + Objective: 9.0 + x = 1.0, ReducedCost = 0.0 + y = 3.0, ReducedCost = 0.0 + z = 0.0, ReducedCost = 2.999999858578644 + c1 DualValue = 1.0000000592359144 + c2 DualValue = 1.0000000821854418 +""" + +from cuopt.linear_programming.problem import Problem, MINIMIZE + + +def main(): + """Run the simple LP example.""" + problem = Problem("min_dual_rc") + + # Add Variables + x = problem.addVariable(lb=0.0, name="x") + y = problem.addVariable(lb=0.0, name="y") + z = problem.addVariable(lb=0.0, name="z") + + # Add Constraints (equalities) + problem.addConstraint(x + y + z == 4.0, name="c1") + problem.addConstraint(2.0 * x + y + z == 5.0, name="c2") + + # Set Objective (minimize) + problem.setObjective(3.0 * x + 2.0 * y + 5.0 * z, sense=MINIMIZE) + + # Solve + problem.solve() + + # Check solution status + if problem.Status.name == "Optimal": + print(f"Optimal solution found in {problem.SolveTime:.2f} seconds") + # Get Primal + print("Objective:", problem.ObjValue) + for v in problem.getVariables(): + print( + f"{v.VariableName} = {v.Value}, ReducedCost = {v.ReducedCost}" + ) + # Get Duals + for c in problem.getConstraints(): + print(f"{c.ConstraintName} DualValue = {c.DualValue}") + else: + print(f"Problem status: {problem.Status.name}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: + +## Working with PDLP Warmstart Data + +Warmstart data allows to restart PDLP with a previous solution context. This should be used when you solve a new problem which is similar to the previous one. + + +Warmstart data is only available for Linear Programming (LP) problems, not for Mixed Integer Linear Programming (MILP) problems. + + +{/* embed: examples/pdlp_warmstart_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Working with PDLP Warmstart Data Example + +This example demonstrates: +- Using PDLP (Primal-Dual hybrid gradient for LP) solver method +- Extracting warmstart data from a solved problem +- Reusing warmstart data to solve a similar problem faster +- Comparing solve times with and without warmstart + +Warmstart data allows restarting PDLP with a previous solution context. +This should be used when you solve a new problem which is similar to the +previous one. + +Note: + Warmstart data is only available for Linear Programming (LP) problems, + not for Mixed Integer Linear Programming (MILP) problems. + +Problem 1: + Maximize: 2*x + y + Subject to: + 4*x + 10*y <= 130 + 8*x - 3*y >= 40 + x, y >= 0 + +Problem 2 (similar): + Maximize: 2*x + y + Subject to: + 4*x + 10*y <= 100 + 8*x - 3*y >= 50 + x, y >= 0 + +Expected Output: + Optimal solution found in 0.01 seconds + x = 25.0 + y = 0.0 + Objective value = 50.0 +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver.solver_parameters import ( + CUOPT_METHOD, + CUOPT_PDLP_SOLVER_MODE, +) +from cuopt.linear_programming.solver_settings import ( + SolverSettings, + SolverMethod, + PDLPSolverMode, +) + + +def main(): + """Run the PDLP warmstart example.""" + print("=== Solving Problem 1 ===") + + # Create a new problem + problem = Problem("Simple LP") + + # Add variables + x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + + # Add constraints + problem.addConstraint(4 * x + 10 * y <= 130, name="c1") + problem.addConstraint(8 * x - 3 * y >= 40, name="c2") + + # Set objective function + problem.setObjective(2 * x + y, sense=MAXIMIZE) + + # Configure solver settings + settings = SolverSettings() + settings.set_parameter(CUOPT_METHOD, SolverMethod.PDLP) + settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Stable2) + + # Solve the problem + problem.solve(settings) + + print(f"Problem 1 solved in {problem.SolveTime:.4f} seconds") + print(f"x = {x.getValue()}, y = {y.getValue()}") + print(f"Objective value = {problem.ObjValue}") + + # Get the warmstart data + warmstart_data = problem.getWarmstartData() + print( + f"\nWarmstart data extracted (primal solution size: " + f"{len(warmstart_data.current_primal_solution)})" + ) + + print("\n=== Solving Problem 2 with Warmstart ===") + + # Create a new problem + new_problem = Problem("Warmstart LP") + + # Add variables + x = new_problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = new_problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + + # Add constraints (slightly different from problem 1) + new_problem.addConstraint(4 * x + 10 * y <= 100, name="c1") + new_problem.addConstraint(8 * x - 3 * y >= 50, name="c2") + + # Set objective function + new_problem.setObjective(2 * x + y, sense=MAXIMIZE) + + # Configure solver settings with warmstart data + settings.set_pdlp_warm_start_data(warmstart_data) + + # Solve the problem + new_problem.solve(settings) + + # Check solution status + if new_problem.Status.name == "Optimal": + print(f"Optimal solution found in {new_problem.SolveTime:.2f} seconds") + print(f"x = {x.getValue()}") + print(f"y = {y.getValue()}") + print(f"Objective value = {new_problem.ObjValue}") + else: + print(f"Problem status: {new_problem.Status.name}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: diff --git a/docs/cuopt/source/cuopt-python/convex/examples/expressions_constraints_example.py b/fern/docs/pages/cuopt-python/convex/examples/expressions_constraints_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/expressions_constraints_example.py rename to fern/docs/pages/cuopt-python/convex/examples/expressions_constraints_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/general_quadratic_example.py b/fern/docs/pages/cuopt-python/convex/examples/general_quadratic_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/general_quadratic_example.py rename to fern/docs/pages/cuopt-python/convex/examples/general_quadratic_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/mps_example.py b/fern/docs/pages/cuopt-python/convex/examples/mps_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/mps_example.py rename to fern/docs/pages/cuopt-python/convex/examples/mps_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/pdlp_warmstart_example.py b/fern/docs/pages/cuopt-python/convex/examples/pdlp_warmstart_example.py similarity index 96% rename from docs/cuopt/source/cuopt-python/convex/examples/pdlp_warmstart_example.py rename to fern/docs/pages/cuopt-python/convex/examples/pdlp_warmstart_example.py index 6ad4d946f7..ccb8f7c1d4 100644 --- a/docs/cuopt/source/cuopt-python/convex/examples/pdlp_warmstart_example.py +++ b/fern/docs/pages/cuopt-python/convex/examples/pdlp_warmstart_example.py @@ -14,9 +14,8 @@ This should be used when you solve a new problem which is similar to the previous one. -Note: - Warmstart data is only available for Linear Programming (LP) problems, - not for Mixed Integer Linear Programming (MILP) problems. +**Note:** Warmstart data is only available for Linear Programming (LP) problems, +not for Mixed Integer Linear Programming (MILP) problems. Problem 1: Maximize: 2*x + y diff --git a/docs/cuopt/source/cuopt-python/convex/examples/production_planning_example.py b/fern/docs/pages/cuopt-python/convex/examples/production_planning_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/production_planning_example.py rename to fern/docs/pages/cuopt-python/convex/examples/production_planning_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/qp_matrix_example.py b/fern/docs/pages/cuopt-python/convex/examples/qp_matrix_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/qp_matrix_example.py rename to fern/docs/pages/cuopt-python/convex/examples/qp_matrix_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/rotated_socp_example.py b/fern/docs/pages/cuopt-python/convex/examples/rotated_socp_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/rotated_socp_example.py rename to fern/docs/pages/cuopt-python/convex/examples/rotated_socp_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/sample.mps b/fern/docs/pages/cuopt-python/convex/examples/sample.mps similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/sample.mps rename to fern/docs/pages/cuopt-python/convex/examples/sample.mps diff --git a/docs/cuopt/source/cuopt-python/convex/examples/simple_lp_example.py b/fern/docs/pages/cuopt-python/convex/examples/simple_lp_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/simple_lp_example.py rename to fern/docs/pages/cuopt-python/convex/examples/simple_lp_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/simple_qp_example.py b/fern/docs/pages/cuopt-python/convex/examples/simple_qp_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/simple_qp_example.py rename to fern/docs/pages/cuopt-python/convex/examples/simple_qp_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/simple_socp_example.py b/fern/docs/pages/cuopt-python/convex/examples/simple_socp_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/simple_socp_example.py rename to fern/docs/pages/cuopt-python/convex/examples/simple_socp_example.py diff --git a/docs/cuopt/source/cuopt-python/convex/examples/solution_example.py b/fern/docs/pages/cuopt-python/convex/examples/solution_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/convex/examples/solution_example.py rename to fern/docs/pages/cuopt-python/convex/examples/solution_example.py diff --git a/fern/docs/pages/cuopt-python/convex/index.mdx b/fern/docs/pages/cuopt-python/convex/index.mdx new file mode 100644 index 0000000000..5f6506c065 --- /dev/null +++ b/fern/docs/pages/cuopt-python/convex/index.mdx @@ -0,0 +1,5 @@ +--- +title: "Convex Optimization (LP/QP/QCQP/SOCP)" +--- + +This section contains details on the cuOpt convex optimization Python API. diff --git a/fern/docs/pages/cuopt-python/index.mdx b/fern/docs/pages/cuopt-python/index.mdx new file mode 100644 index 0000000000..1a7c138532 --- /dev/null +++ b/fern/docs/pages/cuopt-python/index.mdx @@ -0,0 +1,7 @@ +--- +title: "Python API" +--- + +NVIDIA cuOpt supports Python API for routing optimization, convex optimization, and mixed-integer programming. + +This section contains details on the cuOpt Python package. diff --git a/docs/cuopt/source/cuopt-python/mip/examples/incumbent_solutions_example.py b/fern/docs/pages/cuopt-python/mip/examples/incumbent_solutions_example.py similarity index 96% rename from docs/cuopt/source/cuopt-python/mip/examples/incumbent_solutions_example.py rename to fern/docs/pages/cuopt-python/mip/examples/incumbent_solutions_example.py index 1ef73669d3..d65cf4fa13 100644 --- a/docs/cuopt/source/cuopt-python/mip/examples/incumbent_solutions_example.py +++ b/fern/docs/pages/cuopt-python/mip/examples/incumbent_solutions_example.py @@ -16,9 +16,8 @@ solving process. They represent the best integer-feasible solution discovered so far. -Note: - Incumbent solutions are only available for Mixed Integer Programming (MIP) - problems, not for pure Linear Programming (LP) problems. +**Note:** Incumbent solutions are only available for Mixed Integer Programming +(MIP) problems, not for pure Linear Programming (LP) problems. Problem: Maximize: 5*x + 3*y diff --git a/docs/cuopt/source/cuopt-python/mip/examples/semi_continuous_example.py b/fern/docs/pages/cuopt-python/mip/examples/semi_continuous_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/mip/examples/semi_continuous_example.py rename to fern/docs/pages/cuopt-python/mip/examples/semi_continuous_example.py diff --git a/docs/cuopt/source/cuopt-python/mip/examples/simple_milp_example.py b/fern/docs/pages/cuopt-python/mip/examples/simple_milp_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/mip/examples/simple_milp_example.py rename to fern/docs/pages/cuopt-python/mip/examples/simple_milp_example.py diff --git a/fern/docs/pages/cuopt-python/mip/index.mdx b/fern/docs/pages/cuopt-python/mip/index.mdx new file mode 100644 index 0000000000..046777abf5 --- /dev/null +++ b/fern/docs/pages/cuopt-python/mip/index.mdx @@ -0,0 +1,9 @@ +--- +title: "MIP (Mixed Integer Programming)" +--- + + +The cuOpt MIP solver is in **beta** and under active development. The solver currently excels at finding high-quality feasible solutions quickly with GPU-accelerated primal heuristics. Proving feasible solutions optimal remains under active development. + + +This section contains details on the cuOpt MIP Python API. diff --git a/fern/docs/pages/cuopt-python/mip/mip-api.mdx b/fern/docs/pages/cuopt-python/mip/mip-api.mdx new file mode 100644 index 0000000000..cae5ba4e9f --- /dev/null +++ b/fern/docs/pages/cuopt-python/mip/mip-api.mdx @@ -0,0 +1,1063 @@ +--- +title: "MIP Python API Reference" +--- + + +## Data Model + +
+ +### class `DataModel` + +Initialize a DataModel which represents a Linear Program. + +A linear programming optimization problem is defined as follows: Minimize : dot(c, x) Subject to : matmul(A, x) (= or >= or)<= b Where : x : Decision Variables c : Objective Coefficients A : Constraint Matrix b : Constraint Bounds With : n = number of variables m = number of constraints x = n-dim vector c = n-dim vector A = mxn-dim sparse matrix b = m-dim vector + +**Notes** + +By default, this assumes objective minimization. +To solve a maximization problem, see set_maximization() + +Objective value can be scaled and offseted accordingly: +objective_scaling_factor * (dot(c, x) + objective_offset) +please refer to to the `set_objective_scaling_factor()` +and `set_objective_offset()` method. + +**Examples** + +```python +Minimize: + + cost = 0.2 * VAR1 + 0.1 * VAR2 + +Subject to + + 3 * VAR1 + 4 * VAR2 <= 5.4 + + 2.7 * VAR1 + 10.1 * VAR2 <= 4.9 + + 0 <= VAR1 <= 2 + + 0 <= VAR2 <= inf + +>>> from cuopt import linear_programming +>>> +>>> import numpy as np +>>> +>>> data_model = linear_programming.DataModel() +>>> +>>> # Set the CSR matrix representation, for more information about CSR +>>> # checkout: +>>> # https://docs.nvidia.com/cuda/cusparse/index.html +#compressed-sparse-row-csr +>>> +>>> # Define the different np.array for the CSR representation +>>> # The 4 values of the constraint matrix (A) +>>> A_values = np.array([3.0, 4.0, 2.7, 10.1], dtype=np.float64) +>>> +>>> # The CSR index vector +>>> # Here we associate each value in the A_values to its variable index +>>> # First value correspond to the first variable +>>> # (3.0 -> variable[*0*], constraint[0]) +>>> # Second value correspond to the second variable +>>> # (4.0 -> variable[*1*], constraint[0]) +>>> # Third value correspond to the first variable +>>> # (2.7 -> variable[*0*], constraint[1]) +>>> # Fourth value correspond to the second variable +>>> # (10.1 -> variable[*1*], constraint[1]) +>>> A_indices = np.array([0, 1, 0, 1], dtype=np.int32) +>>> +>>> # The CSR offset vector +>>> # Here we specify the range of values for each constraint +>>> # [0, 2) corresponds to the range of values for the first constraints, +>>> # here [0:3.0, 1:4.0] +>>> # [2, 4) corresponds to the range of values for the second constraint, +>>> # here [1:2.7, 2:10.1] +>>> A_offsets = np.array([0, 2, 4], dtype=np.int32) +>>> +>>> data_model.set_csr_constraint_matrix(A_values, A_indices, A_offsets) +>>> +>>> # Set the constraint bounds (b / right-hand side) array +>>> b = np.array([5.4, 4.9], dtype=np.float64) +>>> data_model.set_constraint_bounds(b) +>>> +>>> # Set the objective coefficient (c) array. +>>> c = np.array([0.2, 0.1], dtype=np.float64) +>>> data_model.set_objective_coefficients(c) +>>> +>>> # Set the constraints/rows equalities, either using the row_type format +>>> # or by directly setting the bounds +>>> +>>> # Method 0: constraints/rows type +>>> # Set both constraints/rows to less-than (<=) +>>> row_types = np.array(['L', 'L']) +>>> data_model.set_row_types(row_types) +>>> +>>> # Method 1: directly set bounds +>>> # Set lower bounds to -infinity and upper bounds to b +>>> constraint_lower_bounds = np.array([-np.inf, -np.inf], +>>> dtype=np.float64) +>>> constraint_upper_bounds = np.array(b, dtype=np.float64) +>>> data_model.set_constraint_lower_bounds(constraint_lower_bounds) +>>> data_model.set_constraint_upper_bounds(constraint_upper_bounds) +>>> +>>> +>>> # Set variable lower and upper bounds +>>> variable_lower_bounds = np.array([0.0, 0.0], dtype=np.float64) +>>> variable_upper_bounds = np.array([2.0, np.inf], dtype=np.float64) +>>> data_model.set_variable_lower_bounds(variable_lower_bounds) +>>> data_model.set_variable_upper_bounds(variable_upper_bounds) +``` + + +#### def `__init__` + +```python +__init__() +``` + +#### def `set_maximize` + +```python +set_maximize(maximize) +``` + +Set the sense of optimization to maximize. + +**Parameters** + +- **`maximize`** (`bool`) — True means to maximize the objective function, else minimize. + +**Notes** + +Setting before calling the solver is optional, default value if false +(minimize). + + +#### def `set_csr_constraint_matrix` + +```python +set_csr_constraint_matrix(A_values, A_indices, A_offsets) +``` + +Set the constraint matrix (A) in CSR format. For more information about CSR checkout: https://docs.nvidia.com/cuda/cusparse/index.html#compressed-sparse-row-csr #noqa + +**Parameters** + +- **`A_values`** (`np.array dtype - float64`) — Values of the CSR representation of the constraint matrix as a device floating point array. +- **`A_indices`** (`np.array dtype - int32`) — Indices of the CSR representation of the constraint matrix as a device integer array. +- **`A_offsets`** (`np.array dtype - int32`) — Offsets of the CSR representation of the constraint matrix as a device integer array. + +**Notes** + +Setting before calling the solver is mandatory. + + +#### def `set_constraint_bounds` + +```python +set_constraint_bounds(b) +``` + +Set the constraint bounds (b / right-hand side) array. + +**Parameters** + +- **`b`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is mandatory. + + +#### def `set_objective_coefficients` + +```python +set_objective_coefficients(c) +``` + +Set the objective coefficients (c) array. + +**Parameters** + +- **`c`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is mandatory. + + +#### def `set_objective_scaling_factor` + +```python +set_objective_scaling_factor(objective_scaling_factor) +``` + +Set the scaling factor of the objective function (scaling_factor * objective_value). + +**Parameters** + +- **`objective_scaling_factor`** (`float64`) — The scaling factor to apply. + +**Notes** + +Setting before calling the solver is optional. + + +#### def `set_objective_offset` + +```python +set_objective_offset(objective_offset) +``` + +Set the offset of the objective function (objective_offset + objective_value). + +**Parameters** + +- **`objective_offset`** (`float64`) — The constant objective_offset to add. + +**Notes** + +Setting before calling the solver is optional. + + +#### def `set_quadratic_objective_matrix` + +```python +set_quadratic_objective_matrix(Q_values, Q_indices, Q_offsets) +``` + +Set the quadratic objective matrix (Q) in CSR format. For more information about CSR checkout: https://docs.nvidia.com/cuda/cusparse/index.html#compressed-sparse-row-csr #noqa + +**Parameters** + +- **`Q_values`** (`np.array dtype - float64`) — Values of the CSR representation of the quadratic objective matrix as a device floating point array. +- **`Q_indices`** (`np.array dtype - int32`) — Indices of the CSR representation of the quadratic objective matrix as a device integer array. +- **`Q_offsets`** (`np.array dtype - int32`) — Offsets of the CSR representation of the quadratic objective matrix as a device integer array. (size: n_variables + 1) + +**Notes** + +Setting before calling the solver is required for quadratic problems. +The input Q matrix is internally symmetrized as Q + Q^T. For convex +quadratic programming, Q + Q^T should be positive semi-definite. + + +#### def `get_quadratic_constraints` + +```python +get_quadratic_constraints() +``` + +Return quadratic constraints appended to this model. + +Each entry is a dict with keys including ``constraint_row_index``, ``constraint_row_name``, ``constraint_row_type``, COO arrays, and ``rhs_value``. + + +#### def `clear_quadratic_constraints` + +```python +clear_quadratic_constraints() +``` + +Remove all quadratic constraints from the model. + + +#### def `add_quadratic_constraint` + +```python +add_quadratic_constraint(constraint_row_name = '', linear_values = None, linear_indices = None, rhs_value = 0.0, vals = None, rows = None, cols = None, sense = 'L') +``` + +Add a quadratic constraint. + +Each constraint has a linear part (optional) and a quadratic part in COO format. Call multiple times to add several quadratic constraints. + +**Parameters** + +- **`constraint_row_name`** (`str, optional`) — Optional row name. +- **`linear_values, linear_indices`** (`array-like, optional`) — Sparse linear coefficients on the same variable index space. +- **`rhs_value`** (`float, optional`) — Right-hand side of the constraint. +- **`vals, rows, cols`** (`array-like`) — COO triplets for the quadratic matrix Q in ``linear^T x + x^T Q x {sense} rhs_value``. +- **`sense`** (`str, optional`) — Constraint sense: ``'L'`` (default, ``<=``) or ``'G'`` (``>=``). ``'G'`` constraints are converted to ``'L'`` internally. Equality (``'E'``) is not supported. + +**Notes** + +When any quadratic constraint is present, cuOpt selects the barrier +solver and converts quadratic constraints to second-order cones. + + +#### def `set_variable_lower_bounds` + +```python +set_variable_lower_bounds(variable_lower_bounds) +``` + +Set the variables (x) lower bounds. + +**Parameters** + +- **`variable_lower_bounds`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional, default value for all is +0. + + +#### def `set_variable_upper_bounds` + +```python +set_variable_upper_bounds(variable_upper_bounds) +``` + +Set the variables (x) upper bounds. + +**Parameters** + +- **`variable_upper_bounds`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional, default value for all is ++infinity. + + +#### def `set_constraint_lower_bounds` + +```python +set_constraint_lower_bounds(constraint_lower_bounds) +``` + +Set the constraints lower bounds. + +**Parameters** + +- **`constraint_lower_bounds`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional if you set the row type, +else it's mandatory along with the upper bounds. + + +#### def `set_constraint_upper_bounds` + +```python +set_constraint_upper_bounds(constraint_upper_bounds) +``` + +Set the constraints upper bounds. + +**Parameters** + +- **`constraint_upper_bounds`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional if you set the row type, +else it's mandatory along with the lower bounds. + + +#### def `set_variable_types` + +```python +set_variable_types(variable_types) +``` + +Set the variable types. + +**Parameters** + +- **`variables_types`** (`np.array dtype - unicode string (= ): lower constrains bound equal to b, upper constrains bound equal to +infinity + +**Parameters** + +- **`row_types`** (`np.array dtype - unicode string (>> row_types = np.array(['L', 'L']) +>>> data_model.set_row_types(row_types) +``` + + +#### def `set_variable_names` + +```python +set_variable_names(variables_names) +``` + +Set the variables names. + +**Parameters** + +- **`variables_names`** (`np.array dtype - unicode string`) — Host string array. + +**Notes** + +Setting before calling the solver is optional. Value is only used for +file generation of the solution. + + +#### def `set_row_names` + +```python +set_row_names(row_names) +``` + +Set the row names. + +**Parameters** + +- **`row_names`** (`np.array dtype - unicode string`) — Host string array. + +**Notes** + +Setting before calling the solver is optional. Value is only used for +file generation of the solution. + + +#### def `set_objective_name` + +```python +set_objective_name(objective_name) +``` + +Set the objective name as string. + + +#### def `set_problem_name` + +```python +set_problem_name(problem_name) +``` + +Set the problem name as string. + + +#### def `set_initial_primal_solution` + +```python +set_initial_primal_solution(initial_primal_solution) +``` + +Set the initial primal solution. + +**Parameters** + +- **`initial_primal_solution`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional. + + +#### def `set_initial_dual_solution` + +```python +set_initial_dual_solution(initial_dual_solution) +``` + +NOTE: Not supported for MILP. + +Set the initial dual solution. + +**Parameters** + +- **`initial_dual_solution`** (`np.array dtype - float64`) — Device floating point array. + +**Notes** + +Setting before calling the solver is optional. + + +#### def `get_sense` + +```python +get_sense() +``` + +Get the sense of optimization as a bool. True means maximize the objective function, false means minimize. + + +#### def `get_constraint_matrix_values` + +```python +get_constraint_matrix_values() +``` + +Get the values of the CSR representation of the constraint matrix as numpy.array with float64 type. + + +#### def `get_constraint_matrix_indices` + +```python +get_constraint_matrix_indices() +``` + +Get the indices of the CSR representation of the constraint matrix as numpy.array with int type. + + +#### def `get_constraint_matrix_offsets` + +```python +get_constraint_matrix_offsets() +``` + +Get the indices of the CSR representation of the constraint matrix as numpy.array with int type. + + +#### def `get_constraint_bounds` + +```python +get_constraint_bounds() +``` + +Get the constraint bounds (b / right-hand side) as numpy.array with float64 type. + + +#### def `get_objective_coefficients` + +```python +get_objective_coefficients() +``` + +Get the objective_coefficients (c) as numpy.array with float64 type. + + +#### def `get_objective_scaling_factor` + +```python +get_objective_scaling_factor() +``` + +Get the scaling factor of the objective function as a float64. + + +#### def `get_objective_offset` + +```python +get_objective_offset() +``` + +Get the offset of the objective function as a float64. + + +#### def `get_quadratic_objective_values` + +```python +get_quadratic_objective_values() +``` + +Get the values of the CSR representation of the quadratic objective matrix as a numpy.array with float64 type. + + +#### def `get_quadratic_objective_indices` + +```python +get_quadratic_objective_indices() +``` + +Get the indices of the CSR representation of the quadratic objective matrix as a numpy.array with int32 type. + + +#### def `get_quadratic_objective_offsets` + +```python +get_quadratic_objective_offsets() +``` + +Get the offsets of the CSR representation of the quadratic objective matrix as a numpy.array with int32 type. + + +#### def `get_variable_lower_bounds` + +```python +get_variable_lower_bounds() +``` + +Get the variables (x) lower bounds as numpy.array with float64 type. + + +#### def `get_variable_upper_bounds` + +```python +get_variable_upper_bounds() +``` + +Get the variables (x) upper bounds as numpy.array with float64 type. + + +#### def `get_constraint_lower_bounds` + +```python +get_constraint_lower_bounds() +``` + +Get the constraints lower bounds as numpy.array with float64 type. + + +#### def `get_constraint_upper_bounds` + +```python +get_constraint_upper_bounds() +``` + +Get the constraints upper bounds as numpy.array with float64 type. + + +#### def `get_row_types` + +```python +get_row_types() +``` + +Get the type of each row (constraint) as numpy.array with char8 type. + + +#### def `get_ascii_row_types` + +```python +get_ascii_row_types() +``` + +Get the type of each row (constraint) converted to a numpy.array with int8 type (ascii value). + + +#### def `get_initial_primal_solution` + +```python +get_initial_primal_solution() +``` + +Get the initial primal solution as numpy.array with float64 type. + + +#### def `get_initial_dual_solution` + +```python +get_initial_dual_solution() +``` + +NOTE: Not supported for MILP. + +Get the initial dual solution as numpy.array with float64 type. + + +#### def `get_variable_types` + +```python +get_variable_types() +``` + +Get the variable types as numpy.array with char type. + + +#### def `get_variable_names` + +```python +get_variable_names() +``` + +Get the variable names as numpy.array with string type. + + +#### def `get_row_names` + +```python +get_row_names() +``` + +Get the row names as numpy.array with string type. + + +#### def `get_objective_name` + +```python +get_objective_name() +``` + +Get the objective name as string. + + +#### def `get_problem_name` + +```python +get_problem_name() +``` + +Get the problem name as string. + + +#### def `writeMPS` + +```python +writeMPS(user_problem_file) +``` + + +## Solution + +
+ +### class `PDLPWarmStartData` + +#### def `__init__` + +```python +__init__(current_primal_solution, current_dual_solution, initial_primal_average, initial_dual_average, current_ATY, sum_primal_solutions, sum_dual_solutions, last_restart_duality_gap_primal_solution, last_restart_duality_gap_dual_solution, initial_primal_weight, initial_step_size, total_pdlp_iterations, total_pdhg_iterations, last_candidate_kkt_score, last_restart_kkt_score, sum_solution_weight, iterations_since_last_restart) +``` + +
+ +### class `Solution` + +A container of LP solver output + +**Parameters** + +- **`problem_category`** (`int`) — Whether it is a LP-0, MIP-1 or IP-2 solution +- **`vars`** (`Dict[str, float64]`) — Dictionary mapping each variable (name) to its value. +- **`primal_solution`** (`numpy.array`) — Primal solution of the LP problem +- **`dual_solution`** (`numpy.array`) — Note: Applicable to only LP Dual solution of the LP problem +- **`reduced_cost`** (`numpy.array`) — Note: Applicable to only LP The reduced cost. It contains the dual multipliers for the linear constraints. +- **`termination_status: Integer`** — Termination status value. +- **`primal_residual: Float64`** — L2 norm of the primal residual: measurement of the primal infeasibility +- **`dual_residual: Float64`** — Note: Applicable to only LP L2 norm of the dual residual: measurement of the dual infeasibility +- **`primal_objective: Float64`** — Value of the primal objective +- **`dual_objective: Float64`** — Note: Applicable to only LP Value of the dual objective +- **`gap: Float64`** — Difference between the primal and dual objective +- **`nb_iterations: Int`** — Number of iterations the LP solver did before converging +- **`mip_gap: float64`** — Note: Applicable to only MILP The relative difference between the best integer objective value found so far and the objective bound. A value of 0.01 means the solution is guaranteed to be within 1% of optimal. +- **`solution_bound: float64`** — Note: Applicable to only MILP The best known bound on the optimal objective value. For minimization problems, this is a lower bound on the optimal value. For maximization problems, this is an upper bound. +- **`max_constraint_violation: float64`** — Note: Applicable to only MILP The maximum amount by which any constraint is violated in the current solution. Should be close to zero for a feasible solution. +- **`max_int_violation: float64`** — Note: Applicable to only MILP The maximum amount by which any integer variable deviates from being an integer. A value of 0 means all integer variables have integral values. +- **`max_variable_bound_violation: float64`** — Note: Applicable to only MILP The maximum amount by which any variable violates its upper or lower bounds in the current solution. Should be zero for a feasible solution. +- **`presolve_time: float64`** — Note: Applicable to only MILP Time used for pre-solve +- **`solve_time: Float64`** — Solve time in seconds +- **`solved_by: enum`** — Note: Applicable to only LP Whether the LP was solved by Dual Simplex, PDLP or Barrier. This is populated by the solver using the values from SolverMethod. + + +#### def `__init__` + +```python +__init__(problem_category, vars, solve_time = 0.0, primal_solution = None, dual_solution = None, reduced_cost = None, current_primal_solution = None, current_dual_solution = None, initial_primal_average = None, initial_dual_average = None, current_ATY = None, sum_primal_solutions = None, sum_dual_solutions = None, last_restart_duality_gap_primal_solution = None, last_restart_duality_gap_dual_solution = None, initial_primal_weight = 0.0, initial_step_size = 0.0, total_pdlp_iterations = 0, total_pdhg_iterations = 0, last_candidate_kkt_score = 0.0, last_restart_kkt_score = 0.0, sum_solution_weight = 0.0, iterations_since_last_restart = 0, termination_status = 0, error_status = 0, error_message = '', primal_residual = 0.0, dual_residual = 0.0, primal_objective = 0.0, dual_objective = 0.0, gap = 0.0, nb_iterations = 0, solved_by = SolverMethod.Unset, mip_gap = 0.0, solution_bound = 0.0, presolve_time = 0.0, max_constraint_violation = 0.0, max_int_violation = 0.0, max_variable_bound_violation = 0.0, num_nodes = 0, num_simplex_iterations = 0) +``` + +#### def `raise_if_milp_solution` + +```python +raise_if_milp_solution(function_name) +``` + +#### def `raise_if_lp_solution` + +```python +raise_if_lp_solution(function_name) +``` + +#### def `get_primal_solution` + +```python +get_primal_solution() +``` + +Returns the primal solution as numpy.array with float64 type. + + +#### def `get_dual_solution` + +```python +get_dual_solution() +``` + +Note: Applicable to only LP Returns the dual solution as numpy.array with float64 type. + + +#### def `get_primal_objective` + +```python +get_primal_objective() +``` + +Returns the primal objective as a float64. + + +#### def `get_dual_objective` + +```python +get_dual_objective() +``` + +Note: Applicable to only LP Returns the dual objective as a float64. + + +#### def `get_termination_status` + +```python +get_termination_status() +``` + +Returns the termination status as per TerminationReason. + + +#### def `get_termination_reason` + +```python +get_termination_reason() +``` + +Returns the termination reason as per TerminationReason. + + +#### def `get_error_status` + +```python +get_error_status() +``` + +Returns the error status as per ErrorStatus. + + +#### def `get_error_message` + +```python +get_error_message() +``` + +Returns the error message as per ErrorMessage. + + +#### def `get_solve_time` + +```python +get_solve_time() +``` + +Returns the engine solve time in seconds as a float64. + + +#### def `get_solved_by_pdlp` + +```python +get_solved_by_pdlp() +``` + +#### def `get_solved_by` + +```python +get_solved_by() +``` + +Returns whether the LP was solved by Dual Simplex, PDLP or Barrier. See SolverMethod for all possible values. + + +#### def `get_vars` + +```python +get_vars() +``` + +Returns the dictionnary mapping each variable (name) to its value. + + +#### def `get_lp_stats` + +```python +get_lp_stats() +``` + +Note: Applicable to only LP Returns the convergence statistics as a dictionary: + +"primal_residual": float64 Measurement of the primal infeasibility. This quantity is being reduced until primal tolerance is met (see SolverSettings primal_tolerance). "dual_residual": float64, Measurement of the dual infeasibility. This quantity is being reduced until dual tolerance is met (see SolverSettings dual_tolerance). "gap": float64 Difference between the primal and dual objective. This quantity is being reduced until gap tolerance is met (see SolverSettings gap_tolerance). - "nb_iterations": int Number of iterations the LP solver did before converging. + + +#### def `get_reduced_cost` + +```python +get_reduced_cost() +``` + +Note: Applicable to only LP Returns the reduced cost as numpy.array with float64 type. + + +#### def `get_pdlp_warm_start_data` + +```python +get_pdlp_warm_start_data() +``` + +Note: Applicable to only LP + +Allows to retrieve the warm start data from the PDLP solver. See `SolverSettings.set_pdlp_warm_start_data` for more details. + + +#### def `get_milp_stats` + +```python +get_milp_stats() +``` + +Note: Applicable to only MILP Returns the convergence statistics as a dictionary: + +mip_gap: float64 The relative difference between the best integer objective value found so far and the objective bound. A value of 0.01 means the solution is guaranteed to be within 1% of optimal. presolve_time: float64 Time took for pre-solve max_constraint_violation: float64 The maximum amount by which any constraint is violated in the current solution. Should be close to zero for a feasible solution . max_int_violation: float64 The maximum amount by which any integer variable deviates from being an integer. A value of 0 means all integer variables have integral values. max_variable_bound_violation: float64 The maximum amount by which any variable violates its upper or lower bounds in the current solution. Should be zero for a feasible solution. solution_bound: float64 The best known bound on the optimal objective value. For minimization problems, this is a lower bound on the optimal value. For maximization problems, this is an upper bound. num_nodes: int Number of nodes explored during the MIP solve num_simplex_iterations: int Number of simplex iterations performed during the MIP solve + + +#### def `get_problem_category` + +```python +get_problem_category() +``` + +Returns one of the problem category from ProblemCategory + +LP - 0 MIP - 1 IP - 2 + + + +## Solver + +
+ +### def `Solve` + +```python +Solve(data_model, solver_settings = None) +``` + +Solve the Linear Program passed as input and returns the solution. + +Data Model object can be construed through setters (see linear_programming.DataModel class) or through a MPS file (see cuopt.linear_programming.Read function) + +**Parameters** + +- **`data_model`** (`DataModel`) — Data model containing a representation of a linear program on standard form. +- **`solver_settings: SolverSettings`** — Settings to configure solver configurations. By default, it uses default solver settings to solve. + +**Returns** + +- **``** (`solution: Solution`) — Solution object containing both primal and dual solutions, objectives and more statistics about the result. + +**Notes** + +Both primal and dual solutions are zero-initialized. +For custom initialization, please refer to `set_initial_primal_solution()` +and `set_initial_dual_solution()` methods. +For more details on the Solution object, see linear_programming.Solution. + +**Examples** + +```python +>>> from cuopt import linear_programming +>>> from cuopt.linear_programming.solver_settings import PDLPSolverMode +>>> from cuopt.linear_programming.solver.solver_parameters import * +>>> +>>> data_model = linear_programming.DataModel() +>>> +>>> # Set all required fields +>>> data_model.data_model.set_csr_constraint_matrix(...) +>>> +>>> # Build a solver setting object +>>> settings = linear_programming.SolverSettings() +>>> # Lower the accuracy from 1e-4 to 1e-2 for a faster result +>>> settings.set_optimality_tolerance(1e-2) +>>> # Also change the solver mode to Fast1 which can be faster +>>> settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Fast1) +>>> +>>> # Call solve +>>> solution = linear_programming.Solve(data_model, settings) +>>> +>>> # Print solution +>>> print(solution.get_primal_solution()) +>>> # Print the primal objective +>>> print(solution.get_primal_objective()) +>>> # Print the value of one specific variable +>>> print(solution.get_vars()["var_name"]) +``` + + +
+ +### def `BatchSolve` + +```python +BatchSolve(data_model_list, solver_settings = None) +``` + +Solve the list of Linear Programs passed as input and returns the solutions and total solve time. + +.. deprecated:: LP BatchSolve is deprecated and will be removed in a future release. It runs concurrent LPs in multiple C++ threads, which can be done independently in user code. Use sequential :func:`Solve` calls instead, e.g. ``[Solve(dm, solver_settings) for dm in data_model_list]``, or implement your own parallelism (e.g. ``concurrent.futures``). Data Model objects can be construed through setters (see linear_programming.DataModel class) or through a MPS file (see cuopt.linear_programming.Read function) + +**Parameters** + +- **`data_models`** (`list of DataModel`) — Data models containing a representation of a linear program on standard form. +- **`solver_settings: SolverSettings`** — Settings to configure solver configurations. By default, it uses default solver settings to solve. + +**Returns** + +- **``** (`solution: list of Solution`) — List of Solution objects containing both primal and dual solutions, objectives and more statistics about the result. +- **``** (`solve_time: double`) — The engine solve time for the whole batch in seconds as a float64. + +**Notes** + +Solving in batch is usually faster than solving one by one. +The total time to solve the whole batch on the engine side is returned as +summing up the solutions `get_solve_time` would be incorrect as they are +solved together in parallel, overlapping multiple solve. +Both primal and dual solutions are zero-initialized. +For custom initialization, please refer to `set_initial_primal_solution()` +and `set_initial_dual_solution()` methods. +For more details on the Solution object, see linear_programming.Solution. + +**Examples** + +```python +>>> from cuopt import linear_programming +>>> from cuopt.linear_programming.solver_settings import PDLPSolverMode +>>> from cuopt.linear_programming.solver.solver_parameters import * +>>> from cuopt.linear_programming import Read +>>> +>>> data_models = [] +>>> for i in range(...): +>>> data_models.append(Read(...)) +>>> +>>> # Build a solver setting object +>>> settings = linear_programming.SolverSettings() +>>> # Lower the accuracy from 1e-4 to 1e-2 for a faster result +>>> settings.set_optimality_tolerance(1e-2) +>>> # Also change the solver mode to PDLPSolverMode.Fast1 +>>> # which can be faster +>>> settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Fast1) +>>> +>>> # Call solve +>>> solutions, solve_time = linear_programming.BatchSolve( +>>> data_models, +>>> settings +>>> ) +>>> +>>> # Print total engine solve time +>>> print("Total engine solve time: ", solve_time) +>>> # Print solutions +>>> for solution in solutions: +>>> print(solution.get_primal_solution()) +>>> # Print the primal objective +>>> print(solution.get_primal_objective()) +>>> # Print the value of one specific variable +>>> print(solution.get_vars()["var_name"]) +``` diff --git a/fern/docs/pages/cuopt-python/mip/mip-examples.mdx b/fern/docs/pages/cuopt-python/mip/mip-examples.mdx new file mode 100644 index 0000000000..987482cd39 --- /dev/null +++ b/fern/docs/pages/cuopt-python/mip/mip-examples.mdx @@ -0,0 +1,291 @@ +--- +title: "MIP Examples" +--- + +This section contains examples of how to use the cuOpt MIP Python API. + + +The examples in this section are not exhaustive. They are provided to help you get started with the cuOpt mixed integer linear programming Python API. For more examples, please refer to the [cuopt-examples GitHub repository](https://github.com/NVIDIA/cuopt-examples). + + +## Mixed Integer Linear Programming Example + +{/* embed: examples/simple_milp_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Mixed Integer Linear Programming Example + +This example demonstrates how to: +- Create a mixed integer programming problem +- Add integer variables with bounds +- Add constraints with integer variables +- Solve a MIP problem + +Problem: + Maximize: 5*x + 3*y + Subject to: + 2*x + 4*y >= 230 + 3*x + 2*y <= 190 + 10 <= y <= 50 + x, y are integers + +Expected Output: + Optimal solution found in 0.00 seconds + x = 36.0 + y = 41.0 + Objective value = 303.0 +""" + +from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + """Run the simple MIP example.""" + # Create a new MIP problem + problem = Problem("Simple MIP") + + # Add integer variables with bounds + x = problem.addVariable(vtype=INTEGER, name="V_x") + y = problem.addVariable(lb=10, ub=50, vtype=INTEGER, name="V_y") + + # Add constraints + problem.addConstraint(2 * x + 4 * y >= 230, name="C1") + problem.addConstraint(3 * x + 2 * y <= 190, name="C2") + + # Set objective function + problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) + + # Configure solver settings + settings = SolverSettings() + settings.set_parameter("time_limit", 60) + + # Solve the problem + problem.solve(settings) + + # Check solution status and results + if problem.Status.name == "Optimal": + print(f"Optimal solution found in {problem.SolveTime:.2f} seconds") + print(f"x = {x.getValue()}") + print(f"y = {y.getValue()}") + print(f"Objective value = {problem.ObjValue}") + else: + print(f"Problem status: {problem.Status.name}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: + +## Semi-continuous Variable Example + +{/* embed: examples/semi_continuous_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Semi-continuous Variable Example + +This example demonstrates how to: +- Add a semi-continuous variable +- Solve a small MIP that uses the semi-continuous domain + +Problem: + Minimize: x + Subject to: + x + y = 1 + x is either 0 or in [5, 10] + 0 <= y <= 1 + +Expected Output: + Optimal solution found in 0.00 seconds + x = 0.0 + y = 1.0 + Objective value = 0.0 +""" + +from cuopt.linear_programming.problem import ( + CONTINUOUS, + MINIMIZE, + SEMI_CONTINUOUS, + Problem, +) +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + """Run the semi-continuous variable example.""" + problem = Problem("Semi-continuous") + + x = problem.addVariable(lb=5.0, ub=10.0, vtype=SEMI_CONTINUOUS, name="x") + y = problem.addVariable(lb=0.0, ub=1.0, vtype=CONTINUOUS, name="y") + + problem.addConstraint(x + y == 1.0) + problem.setObjective(x, sense=MINIMIZE) + + settings = SolverSettings() + settings.set_parameter("time_limit", 10) + + problem.solve(settings) + + if problem.Status.name == "Optimal": + print(f"Optimal solution found in {problem.SolveTime:.2f} seconds") + print(f"x = {x.getValue()}") + print(f"y = {y.getValue()}") + print(f"Objective value = {problem.ObjValue}") + else: + print(f"Problem status: {problem.Status.name}") + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: + +## Working with Incumbent Solutions + +Incumbent solutions are intermediate feasible solutions found during the MIP solving process. They represent the best integer-feasible solution discovered so far and can be accessed through callback functions. + + +Incumbent solutions are only available for Mixed Integer Programming (MIP) problems, not for pure Linear Programming (LP) problems. + + +{/* embed: examples/incumbent_solutions_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Working with Incumbent Solutions Example + +This example demonstrates: +- Using callbacks to receive intermediate solutions during MIP solving +- Using Problem.getIncumbentValues() to extract variable values from + incumbent solutions +- Tracking solution progress as the solver improves the solution +- Accessing incumbent (best so far) solutions before final optimum +- Custom callback class implementation + +Incumbent solutions are intermediate feasible solutions found during the MIP +solving process. They represent the best integer-feasible solution discovered +so far. + +Note: + Incumbent solutions are only available for Mixed Integer Programming (MIP) + problems, not for pure Linear Programming (LP) problems. + +Problem: + Maximize: 5*x + 3*y + Subject to: + 2*x + 4*y >= 230 + 3*x + 2*y <= 190 + x, y are integers + +Expected Output: + Incumbent 1: x=36.0, y=41.0, cost: 303.00 + + === Final Results === + Problem status: Optimal + Solve time: 0.27 seconds + Final solution: x=36.0, y=41.0 + Final objective value: 303.00 +""" + +from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings +from cuopt.linear_programming.solver.solver_parameters import CUOPT_TIME_LIMIT +from cuopt.linear_programming.internals import GetSolutionCallback + + +class IncumbentCallback(GetSolutionCallback): + """Callback to receive and track incumbent solutions during solving. + + Uses Problem.getIncumbentValues() to extract variable values from the + raw incumbent solution array. + """ + + def __init__(self, problem, variables, user_data): + super().__init__() + self.problem = problem + self.variables = variables + self.solutions = [] + self.n_callbacks = 0 + self.user_data = user_data + + def get_solution(self, solution, solution_cost, solution_bound, user_data): + """Called whenever the solver finds a new incumbent solution.""" + assert user_data is self.user_data + self.n_callbacks += 1 + + # Use getIncumbentValues to extract values for specific variables + values = self.problem.getIncumbentValues(solution, self.variables) + + incumbent = { + "values": values, + "cost": float(solution_cost[0]), + "bound": float(solution_bound[0]), + "iteration": self.n_callbacks, + } + self.solutions.append(incumbent) + + print(f"Incumbent {self.n_callbacks}:", end=" ") + for i, var in enumerate(self.variables): + print(f"{var.VariableName}={values[i]}", end=" ") + print(f"cost: {incumbent['cost']:.2f}") + + +def main(): + """Run the incumbent solutions example.""" + problem = Problem("Incumbent Example") + + # Add integer variables + x = problem.addVariable(vtype=INTEGER, name="x") + y = problem.addVariable(vtype=INTEGER, name="y") + + # Add constraints + problem.addConstraint(2 * x + 4 * y >= 230) + problem.addConstraint(3 * x + 2 * y <= 190) + + # Set objective to maximize + problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) + + # Configure solver settings with callback + settings = SolverSettings() + user_data = {"source": "incumbent_solutions_example"} + incumbent_callback = IncumbentCallback(problem, [x, y], user_data) + settings.set_mip_callback(incumbent_callback, user_data) + settings.set_parameter(CUOPT_TIME_LIMIT, 30) + + # Solve the problem + problem.solve(settings) + + # Display final results + print("\n=== Final Results ===") + print(f"Problem status: {problem.Status.name}") + print(f"Solve time: {problem.SolveTime:.2f} seconds") + print("Final solution: ", end=" ") + for i, var in enumerate(problem.getVariables()): + print(f"{var.VariableName}={var.getValue()} ", end=" ") + print(f"\nFinal objective value: {problem.ObjValue:.2f}") + + print( + f"\nTotal incumbent solutions found: " + f"{len(incumbent_callback.solutions)}" + ) + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +The response is as follows: diff --git a/fern/docs/pages/cuopt-python/quick-start.mdx b/fern/docs/pages/cuopt-python/quick-start.mdx new file mode 100644 index 0000000000..5d62935847 --- /dev/null +++ b/fern/docs/pages/cuopt-python/quick-start.mdx @@ -0,0 +1,23 @@ +--- +title: "Quickstart Guide" +--- + +NVIDIA cuOpt provides a Python API for routing optimization and LP/QP/MILP that enables users to solve complex optimization problems efficiently. + +## Installation + +Choose your install method below; the selector is pre-set for the Python API. Copy the command and run it in your environment. See [Install](../install) for all interfaces and options. + +
+ +### NVIDIA Launchable + +NVIDIA cuOpt can be tested with [NVIDIA Launchable](https://brev.nvidia.com/launchable/deploy?launchableID=env-2qIG6yjGKDtdMSjXHcuZX12mDNJ) with [example notebooks](https://github.com/NVIDIA/cuopt-examples). For more details, please refer to the [NVIDIA Launchable documentation](https://docs.nvidia.com/brev/latest/). + +### Smoke Test + +After installation, you can verify that NVIDIA cuOpt is working correctly by running a simple test. + +Copy and paste this script directly into your terminal ([smoke\_test\_example.sh](routing/examples/smoke_test_example.sh)): + +Example Response: diff --git a/docs/cuopt/source/cuopt-python/routing/examples/intra_factory_example.py b/fern/docs/pages/cuopt-python/routing/examples/intra_factory_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/routing/examples/intra_factory_example.py rename to fern/docs/pages/cuopt-python/routing/examples/intra_factory_example.py diff --git a/docs/cuopt/source/cuopt-python/routing/examples/smoke_test_example.sh b/fern/docs/pages/cuopt-python/routing/examples/smoke_test_example.sh similarity index 100% rename from docs/cuopt/source/cuopt-python/routing/examples/smoke_test_example.sh rename to fern/docs/pages/cuopt-python/routing/examples/smoke_test_example.sh diff --git a/docs/cuopt/source/cuopt-python/routing/examples/tsp_batch_example.py b/fern/docs/pages/cuopt-python/routing/examples/tsp_batch_example.py similarity index 100% rename from docs/cuopt/source/cuopt-python/routing/examples/tsp_batch_example.py rename to fern/docs/pages/cuopt-python/routing/examples/tsp_batch_example.py diff --git a/fern/docs/pages/cuopt-python/routing/index.mdx b/fern/docs/pages/cuopt-python/routing/index.mdx new file mode 100644 index 0000000000..38befbee04 --- /dev/null +++ b/fern/docs/pages/cuopt-python/routing/index.mdx @@ -0,0 +1,5 @@ +--- +title: "Routing Optimization" +--- + +This section contains details on the cuOpt routing optimization Python API. diff --git a/fern/docs/pages/cuopt-python/routing/routing-api.mdx b/fern/docs/pages/cuopt-python/routing/routing-api.mdx new file mode 100644 index 0000000000..2cbbe897e9 --- /dev/null +++ b/fern/docs/pages/cuopt-python/routing/routing-api.mdx @@ -0,0 +1,1865 @@ +--- +title: "cuOpt Routing Python API Reference" +--- + + +## Waypoint Matrix + +
+ +### class `WaypointMatrix` + +Initialize a Waypoint Matrix. + +**Parameters** + +- **`offsets`** (`numpy.ndarray`) — numpy.ndarray of size V + 1 (V: number of vertices). It contains the offsets for the vertices in this graph. Offsets must be in the range [0, E] (E: number of edges). +- **`indices`** (`numpy.ndarray`) — numpy.ndarray of size E (E: number of edges). It contains the destination index for each edge. Destination indices must be in the range [0, V) (V: number of vertices). +- **`weights`** (`numpy.ndarray`) — numpy.ndarray of size E (E: number of edges). It contains the weight value for each edge. The expected type is floating point number. + +**Examples** + +```python +>>> import cuopt +>>> import numpy as np +>>> offsets= np.array([0, 3, 5, 7, 8, 9]) +>>> edges= np.array([1, 2, 3, 0, 2, 0, 3, 4, 0]) +>>> weights= np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) +>>> w_matrix = routing.WaypointMatrix(offsets, edges, weights) +``` + + +#### def `__init__` + +```python +__init__(offsets, indices, weights) +``` + +#### def `compute_cost_matrix` + +```python +compute_cost_matrix(target_locations) +``` + +Compute the cost matrix over the passed graph and target locations. + +This function can be used when the cost matrix is not acquirable due to an incomplete graph. The cost matrix is computed then returned. It can later be used for the Solver (see DataModel.set_matrix). + +**Parameters** + +- **`target_locations`** (`numpy.ndarray`) — numpy.ndarray representing the target locations indices with respect to the graph. Target locations indices must be in the range [0, V-1] (V: number of vertices). + +**Returns** + +- **``** (`cudf.DataFrame`) — cudf.DataFrame representing the cost matrix + +**Raises** + +- **``** — Shape of target_locations needs to be of length 1. +- **``** — Target_locations length must be positive. + +**Examples** + +```python +>>> import cuopt +>>> import numpy as np +>>> offsets= np.array([0, 3, 5, 7, 8, 9]) +>>> edges= np.array([1, 2, 3, 0, 2, 0, 3, 4, 0]) +>>> weights= np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) +>>> w_matrix = routing.WaypointMatrix(offsets, edges, weights) +>>> target_locations = np.array([1, 3]) +>>> cost_matrix = w_matrix.compute_cost_matrix(target_locations) +>>> cost_matrix + 0 1 +0 0.0 7.0 +1 18.0 0.0 +>>> data_model = routing.DataModel(cost_matrix.shape[0], 2) +>>> data_model.set_matrix(cost_matrix) +``` + + +#### def `compute_waypoint_sequence` + +```python +compute_waypoint_sequence(target_locations, route_df) +``` + +Compute the waypoint sequence over the whole route. + +The waypoint sequence is an extend version of the route. Between each route target locations, all the intermediate waypoints are added. Waypoints and target locations ids are based on the graph. A new field is added to route_df to browse through the returned waypoint sequence. The 'sequence_offset' field associates an offset to each element in the route. + +**Parameters** + +- **`target_locations`** (`numpy.ndarray`) — numpy.ndarray representing the target locations indices with respect to the graph. Target locations indices must be in the range [0, V] (V: number of vertices). +- **`route_df: cudf.DataFrame`** — Contains route, vehicle_id, arrival_stamp, and locations. Contains an extra 'sequence_offset' field after this call. + +**Returns** + +- **``** (`cudf.DataFrame`) — waypoint_sequence cudf.Series representing the waypoint_sequence waypoint_type cudf.Series representing type of waypoint Start - Start location of the vehicle/tech/robot End - End location of the vehicle/tech/robot w - Location passing through to get to delivery location Delivery - Location where delivery needs to be made "-" - Separates vehicle sequence from another + +**Raises** + +- **``** — Shape of target_locations needs to be of length 1 +- **``** — Target_locations length must be positive +- **``** — Route length must be positive + +**Notes** + +Calling this function before compute_cost_matrix is an error. + +**Examples** + +```python +>>> import cuopt +>>> import numpy as np +>>> offsets= np.array([0, 3, 5, 7, 8, 9]) +>>> edges= np.array([1, 2, 3, 0, 2, 0, 3, 4, 0]) +>>> weights= np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) +>>> w_matrix = routing.WaypointMatrix(offsets, edges, weights) +>>> # Starting node is considered as depot +>>> target_locations = np.array([0, 1, 3, 4]) +>>> cost_matrix = w_matrix.compute_cost_matrix(target_locations) +>>> cost_matrix + 0 1 2 3 +0 0.0 1.0 3.0 11.0 +1 4.0 0.0 7.0 15.0 +2 17.0 18.0 0.0 8.0 +3 9.0 10.0 12.0 0.0 +>>> data_model = routing.DataModel(cost_matrix.shape[0], 2) +>>> data_model.set_matrix(cost_matrix) +>>> solver = cuopt.Solver(data_model) +>>> solver.set_min_vehicles(2) +>>> solution = solver.solve() +>>> route = solution.get_route() +>>> # Location in this routes is actually ids positional indices of +>>> # target location +>>> route + route arrival_stamp truck_id location +0 0 0.0 1 0 +1 2 3.0 1 2 +2 3 11.0 1 3 +3 0 20.0 1 0 +4 0 0.0 0 0 +5 1 1.0 0 1 +6 0 5.0 0 0 +>>> # Call to this will also update location in route +>>> # with actual target ids along with sequence offset +>>> w_matrix.compute_waypoint_sequence(target_locations, route) + waypoint_sequence waypoint_type +0 0 Start +1 3 Task +2 3 w +3 4 Task +4 4 w +5 0 End +6 0 - +7 0 Start +8 1 Task +9 1 w +10 0 End +>>> # Difference in location and sequence offset in route can be seen +>>> route + route arrival_stamp truck_id location sequence_offset +0 0 0.0 1 0 0 +1 2 3.0 1 3 2 +2 3 11.0 1 4 4 +3 0 20.0 1 0 6 +4 0 0.0 0 0 7 +5 1 1.0 0 1 9 +6 0 5.0 0 0 11 +``` + + +#### def `compute_shortest_path_costs` + +```python +compute_shortest_path_costs(target_locations, weights) +``` + +Compute a custom matrix over the passed weights and target locations. + +This is applied on shortest paths found during previous compute_cost_matrix call. This function allows setting a custom cost between waypoints (for example, time) and then getting the total cost it takes to go from one target location to all the others. The shortest paths are **not** recomputed. The path found from compute_cost_matrix between target locations stays the same but the new weight set is used to compute the output matrix. + +**Parameters** + +- **`target_locations`** (`numpy.ndarray`) — numpy.ndarray representing the target locations indices with respect to the graph. Target locations indices must be in the range [0, V) (V: number of vertices). +- **`weights`** (`numpy.ndarray`) — numpy.ndarray of size E (E: number of edges). It contains the weight value for each edge. The expected type is floating point number. + +**Returns** + +- **``** (`cudf.DataFrame`) — cudf.DataFrame representing the custom cost matrix + +**Raises** + +- **``** — Shape of target_locations needs to be of length 1 +- **``** — Target_locations length must be positive +- **``** — Shape of weights needs to be of length 1 +- **``** — Weights length must be positive +- **``** — Weights length must be positive +- **``** — Given weights and previously set weights length mismatch + +**Notes** + +Giving an edge ordering for weights different from the one given +during waypoint matrix instanciation will lead to incorrect results. + +**Examples** + +```python +>>> import cuopt +>>> import numpy as np +>>> offsets= np.array([ 0, 3, 5, 7, 8, 9]) +>>> edges= np.array([ 1, 2, 3, 0, 2, 0, 3, 4, 0]) +>>> weights= np.array([ 1, 2, 3, 4, 5, 6, 7, 8, 9]) +>>> time_to_travel = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90]) +>>> w_matrix = routing.WaypointMatrix(offsets, edges, weights) +>>> target_locations = np.array([1, 3]) +>>> cost_matrix = w_matrix.compute_cost_matrix(target_locations) +>>> time_matrix = w_matrix.compute_shortest_path_costs( +>>> target_locations, time_to_travel +>>> ) +>>> cost_matrix + 0 1 +0 0.0 7.0 +1 18.0 0.0 +>>> time_matrix + 0 1 +0 0.0 70.0 +1 180.0 0.0 +>>> data_model = routing.DataModel(cost_matrix.shape[0], 2) +>>> data_model.set_matrix(cost_matrix) +>>> data_model.add_transit_time_matrix(time_matrix) +``` + + + +## Vehicle Routing + +
+ +### class `DataModel` + +Initialize a Data Model. + +**Parameters** + +- **`n_locations`** (`Integer`) — number of locations to visit, including vehicle/technician location. +- **`n_fleet`** (`Integer`) — number of vehicles/technician in the fleet. +- **`n_orders`** (`Integer`) — number of orders. +- **`Note:`** — - A cost matrix must be set before passing this object to the solver. - If vehicle locations is not set, then by default 0th index in cost/transit time matrix, time windows, capacity dimension, order location is considered as start and end location of all the vehicles. + +**Examples** + +```python +>>> from cuopt import routing +>>> locations = [0, 1, 2, 3, 4, 5, 6] +>>> vehicles = [0, 1, 2, 3] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +``` + + +#### def `__init__` + +```python +__init__(n_locations, n_fleet, n_orders: int = -1) +``` + +#### def `add_cost_matrix` + +```python +add_cost_matrix(cost_mat, vehicle_type = 0) +``` + +Add a matrix for all locations (vehicle/technician locations included) at once. + +A cost matrix is a square matrix containing the cost of travel which can be distance, time or any other metric, taken pairwise, between all locations. Diagonal elements should be 0. This cost matrix will be used to find the routes through all the locations. The user can call add_cost_matrix multiple times. Setting the vehicle type will enable heterogeneous fleet. It can model traveling distances for different vehicles (bicycles, bikes, trucks). Note: - If vehicle locations is not set, then by default 0th index and column are considered start and end location for all vehicles. + +**Parameters** + +- **`cost_mat`** (`cudf.DataFrame dtype - float32`) — cudf.DataFrame representing floating point square matrix with num_location rows and columns. +- **`vehicle_type`** (`uint8`) — Identifier of the vehicle. +- **`skip_validation`** (`bool`) — If True, skips Python validation for matrix shape, NULL values, and non-negative values. The caller is responsible for providing a valid square matrix matching the number of locations. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> cost_mat_bikes = [ +... [0, 1, 5, 2], +... [2, 0, 7, 4], +... [1, 5, 0, 9], +... [5, 6, 2, 0] +... ] +>>> cost_mat_bikes = cudf.DataFrame(cost_mat_bikes) +>>> cost_mat_bikes + 0 1 2 3 +0 0 1 5 2 +1 2 0 7 4 +2 1 5 0 9 +3 5 6 2 0 +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.add_cost_matrix(cost_mat_bikes, 1) +>>> cost_mat_car = [ +... [0, 1, 2, 1], +... [1, 0, 3, 2], +... [1, 2, 0, 3], +... [1, 3, 9, 0] +... ] +>>> cost_mat_car = cudf.DataFrame(cost_mat_car) +>>> cost_mat_car + 0 1 2 3 +0 0 1 2 1 +1 1 0 3 2 +2 1 2 0 3 +3 1 3 9 0 +>>> data_model.add_cost_matrix(cost_mat_car, 2) +``` + + +#### def `add_transit_time_matrix` + +```python +add_transit_time_matrix(mat, vehicle_type = 0) +``` + +Add transit time matrix for all locations (vehicle/technician locations included) at once. + +This matrix is used to check constraints satisfiability rather than participating in cost optimization. For instance, this matrix can be used to model the time to travel between locations with time windows referring to it while the solver could optimize for cost/distance. A transit time matrix is defined as a square matrix containing the cost, taken pairwise, between all locations. Users should pre-compute time between each pair of locations with their own technique before calling this function. Entries in this matrix could represent time, miles, meters or any metric that can be stored as a real number and satisfies the property above. The user can call add_transit_time_matrix multiple times. Setting the vehicle type will enable heterogeneous fleet. It can model traveling speeds for different vehicles (bicycles, bikes, trucks). Time windows specified in set_order_time_windows will validate the time to travel with secondary matrix if it is available, else primary matrix is used to validate the constraint. Note: - The values provided are considered as units and it is user's responsibility to ensure all time related entries are normalized to one common unit (hours/minutes/seconds/any). - If vehicle locations is not set, then by default 0th index and column are considered start and end location for all vehicles. + +**Parameters** + +- **`mat`** (`cudf.DataFrame dtype - float32`) — cudf.DataFrame representing floating point square matrix with num_location rows and columns. +- **`vehicle_type`** (`uint8`) — Identifier of the vehicle. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> cost_mat = [ +... [0, 1, 5, 2], +... [2, 0, 7, 4], +... [1, 5, 0, 9], +... [5, 6, 2, 0] +... ] +>>> cost_mat = cudf.DataFrame(cost_mat) +>>> cost_mat +0 1 2 3 +0 0 1 5 2 +1 2 0 7 4 +2 1 5 0 9 +3 5 6 2 0 +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.add_cost_matrix(cost_mat, 0) +>>> time_mat = [ +... [0, 10, 50, 20], +... [20, 0, 70, 40], +... [10, 50, 0, 90], +... [50, 60, 20, 0] +... ] +>>> time_mat = cudf.DataFrame(time_mat) +>>> data_model.add_transit_time_matrix(time_mat, 0) +``` + + +#### def `set_break_locations` + +```python +set_break_locations(break_locations) +``` + +The vehicle is allowed to stop at specific locations during a break. It can be at a customer node or another location representing for instance a gas station. The solver will pick the best stop out of all break nodes. The same break node can appear on several routes and satisfy multiple break constraints. + +Note: If the break locations are not set, every location can be used as a break location + +**Parameters** + +- **`break_locations: cudf.Series dtype-int32`** — representing the designated locations that can be used for breaks. The break locations should be numbered in between 0 and nlocations - 1. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> cost_mat = [ +... [0, 1, 5, 2], +... [2, 0, 7, 4], +... [1, 5, 0, 9], +... [5, 6, 2, 0] +... ] +>>> cost_mat = cudf.DataFrame(cost_mat) +>>> cost_mat +0 1 2 3 +0 0 1 5 2 +1 2 0 7 4 +2 1 5 0 9 +3 5 6 2 0 +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.add_cost_matrix(cost_mat) +>>> data_model.set_break_locations(cudf.Series([1, 3])) +``` + + +#### def `add_break_dimension` + +```python +add_break_dimension(break_earliest, break_latest, break_duration) +``` + +Add break time windows to model the Vehicle Routing Problem with Time Windows (VRPTW). The vehicles have break time windows within which the breaks must be taken. And multiple breaks can be added using the same api as another dimension, check the example. + +Note: The values provided are considered as units and it is user's responsibility to ensure all time related entries are normalized to one common unit (hours/minutes/seconds/any). Note: This function cannot be used in conjunction with add_vehicle_break + +**Parameters** + +- **`break_earliest: cudf.Series dtype - int32`** — Earliest time a vehicle can be at a break location. +- **`break_latest: cudf.Series dtype - int32`** — Latest time a vehicle can be at a break location. +- **`break_duration: cudf.Series dtype - int32`** — Time spent at the break location, internally equivalent to service time. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> cost_mat = [ +... [0, 1, 5, 2], +... [2, 0, 7, 4], +... [1, 5, 0, 9], +... [5, 6, 2, 0] +... ] +>>> cost_mat = cudf.DataFrame(cost_mat) +>>> cost_mat +0 1 2 3 +0 0 1 5 2 +1 2 0 7 4 +2 1 5 0 9 +3 5 6 2 0 +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.add_cost_matrix(cost_mat) +>>> time_mat = [ +... [0, 10, 50, 20], +... [20, 0, 70, 40], +... [10, 50, 0, 90], +... [50, 60, 20, 0] +... ] +>>> time_mat = cudf.DataFrame(time_mat) +>>> data_model.add_transit_time_matrix(time_mat) +>>> # Considering vehicles need to take two breaks +>>> lunch_break_earliest = [20, 25] +>>> lunch_break_latest = [40, 45] +>>> lunch_break_service = [5, 5] +>>> data_model.add_break_dimension( +... cudf.Series(lunch_break_earliest), +... cudf.Series(lunch_break_latest), +... cudf.Series(lunch_break_service) +... ) +>>> snack_break_earliest = [40, 45] +>>> snack_break_latest = [60, 65] +>>> snack_break_service = [5, 5] +>>> data_model.add_break_dimension( +... cudf.Series(snack_break_earliest), +... cudf.Series(snack_break_latest), +... cudf.Series(snack_break_service) +``` + + +#### def `add_vehicle_break` + +```python +add_vehicle_break(vehicle_id, earliest, latest, duration, locations = None) +``` + +Specify a break for a given vehicle. Use this api to specify non-homogenous breaks. For example, different number of breaks can be specified for each vehicle by calling this function different number of times for each vehicle. Furthermore, this function provides more flexibility in specifying locations for each break. + +Note: This function cannot be used in conjunction with add_break_dimension + +**Parameters** + +- **`vehicle_id: integer`** — Vehicle Id for which the break is being specified +- **`earliest: integer`** — Earliest time the vehicle can start the break +- **`latest: integer`** — Latest time the vehicle can start the break +- **`duration: integer`** — Time spent at the break location +- **`locations: cudf.Series dtype - int32`** — List of locations where this break can be taken. By default any location can be used + +**Examples** + +```python +>>> from cuopt import routing +>>> vehicle_num = 2 +>>> d = routing.DataModel(nodes, vehicle_num) +>>> d.add_vehicle_break(0, 10, 20, 5, cudf.Series([3, 6, 8])) +>>> d.add_vehicle_break(0, 60, 70, 5, cudf.Series([1, 4, 7])) +>>> d.add_vehicle_break(1, 30, 40, 5) +``` + + +#### def `set_objective_function` + +```python +set_objective_function(objectives, objective_weights) +``` + +The objective function can be defined as a linear combination of the different objectives. Solver optimizes for vehicle count first and then the total objective. The default value of 1 is used for COST objective weight and 0 for other objective weights + +**Parameters** + +- **`objectives`** (`cudf.Series dtype - cuopt.routing.Objective`) — Series of Objective criteria +- **`objective_weights`** (`cudf.Series dtype - float32`) — Series to the weighs associated with the objectives. Series will be cast to float32. + +**Examples** + +```python +>>> from cuopt import routing +>>> d = routing.DataModel(nodes, vehicle_num) +>>> d.set_objective_function( +>>> cudf.Series([routing.Objective.PRIZE, routing.Objective.COST]), +>>> cudf.Series([2**32, 1])) +``` + + +#### def `add_initial_solutions` + +```python +add_initial_solutions(vehicle_ids, routes, types, sol_offsets) +``` + +#### def `set_order_locations` + +```python +set_order_locations(order_locations) +``` + +Set a location for each order. + +This allows the cases with multiple orders per locations run efficiently. Consider an example with 4 locations and 10 orders serving to these 4 locations the order_locations series can look like: [0, 2, 3, 1, 3, 1, 2, 1, 3, 2]. In this case, the ith entry in the series represents the location id of the ith order. Using this, the distance matrix is represented as size 4x4 instead of 10x10. + +**Parameters** + +- **`order_locations`** (`cudf.Series dtype - int32`) — cudf.Series representing location id of each order given as positive integers + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> orders = [0, 2, 3, 1, 3, 1, 2, 1, 3, 2] +>>> data_model = routing.DataModel( +... len(locations), +... len(vehicles), +... len(orders) +... ) +>>> data_model.set_order_locations(cudf.Series(orders)) +``` + + +#### def `set_vehicle_types` + +```python +set_vehicle_types(vehicle_types) +``` + +Set vehicle types in the fleet. + +When multiple matrices are given as input the solver is enabling heterogeneous cost matrix and time matrix optimization. We thus need the corresponding vehicle type id for all vehicles in the data model. + +**Parameters** + +- **`vehicle_types`** (`cudf.Series dtype - uint8`) — cudf.Series representing types of vehicles in the fleet given as positive integers. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> vehicles = [0, 1, 2, 3, 4] +>>> vehicle_types = [0, 1, 1, 0, 0] # 0 - Car 1 - bike +>>> data_model = routing.DataModel( +... len(locations), +... len(vehicles), +... ) +>>> data_model.set_vehicle_types(cudf.Series(vehicle_types)) +``` + + +#### def `set_pickup_delivery_pairs` + +```python +set_pickup_delivery_pairs(pickup_indices, delivery_indices) +``` + +Set pick-up delivery pairs given by indices to the orders. + +Currently mixed pickup and delivery is not supported, meaning that all the orders should be a included in the pick-up delivery pair indices. These indices are indices to order locations set using set_order_locations. + +**Parameters** + +- **`pickup_indices`** (`cudf.Series dtype - int32`) — int cudf.Series representing the indices of pickup orders. +- **`delivery_indices`** (`cudf.Series dtype - int32`) — int cudf.Series representing the indices of delivery orders. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3, 4] +>>> vehicles = [0, 1] +>>> order_locations = [2, 1, 3, 4, 1, 4] +>>> pickup_indices = [0, 2, 4] +>>> delivery_indices = [1, 3, 5] # 2 -> 1, 3 -> 4 and 1->4 +>>> data_model = routing.DataModel( +... len(locations), +... len(vehicles), +... ) +>>> data_model.set_order_locations(order_locations) +>>> data_model.set_pickup_delivery_pairs( +... cudf.Series(pickup_indices), +... cudf.Series(delivery_indices) +... ) +``` + + +#### def `set_vehicle_time_windows` + +```python +set_vehicle_time_windows(earliest_time, latest_time) +``` + +Set vehicle time windows in the fleet. + +The earliest time is the time vehicle can leave the starting location. The latest time is the time vehicle must be free. In case of drop_return_trip, latest time specifies the service end time with the last customer. The size of this array must be equal to fleet_size. Note: The values provided are considered as units and it is user's responsibility to ensure all time related entries are normalized to one common unit (hours/minutes/seconds/any). This would help users to solve for routes which consider vehicle availability time window for each vehicle. If secondary matrix has been set using add_transit_time_matrix, then that will be used for time validation, else primary matrix is used. + +**Parameters** + +- **`earliest_time`** (`cudf.Series dtype - int32`) — cudf.Series representing earliest available times of vehicles +- **`latest_time`** (`cudf.Series dtype - int32`) — cudf.Series representing latest time vehicle must be free. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> cost_mat = [ +... [0, 1, 5, 2], +... [2, 0, 7, 4], +... [1, 5, 0, 9], +... [5, 6, 2, 0] +... ] +>>> cost_mat = cudf.DataFrame(cost_mat) +>>> cost_mat +0 1 2 3 +0 0 1 5 2 +1 2 0 7 4 +2 1 5 0 9 +3 5 6 2 0 +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.add_cost_matrix(cost_mat) +>>> time_mat = [ +... [0, 10, 50, 20], +... [20, 0, 70, 40], +... [10, 50, 0, 90], +... [50, 60, 20, 0] +... ] +>>> time_mat = cudf.DataFrame(time_mat) +>>> data_model.add_transit_time_matrix(time_mat) +>>> veh_earliest = [ 0, 20] # earliest a vehicle/tech start +>>> veh_latest = [200, 180] # end of the vehicle/tech shift +>>> data_model.set_vehicle_time_windows( +... cudf.Series(veh_earliest), +... cudf.Series(veh_latest), +... ) +``` + + +#### def `set_vehicle_locations` + +```python +set_vehicle_locations(start_locations, return_locations) +``` + +Set start and return locations for vehicles in the fleet. + +The start location is a point of start for that vehicle, and return location is designated return location for that vehicle. These can be depot, home or any other locations. The size of these arrays must be equal to fleet_size. When these arrays are not set, all the vehicles in the fleet are assumed to be starting from and returning to depot location, which is zero indexed location. + +**Parameters** + +- **`start_locations `** (`cudf.Series dtype - int32`) — cudf.Series representing starting locations of vehicles +- **`return_locations`** (`cudf.Series dtype - int32`) — cudf.Series representing return locations of vehicles + +**Examples** + +```python +>>> from cuopt import routing +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> vehicle_start_location = [0, 0] +>>> vehicle_end_location = [2, 3] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.set_vehicle_locations( +... cudf.Series(vehicle_start_location), +... cudf.Series(vehicle_end_location) +... ) +``` + + +#### def `set_order_time_windows` + +```python +set_order_time_windows(earliest, latest) +``` + +Add order time windows to model the Vehicle Routing Problem with Time Windows (VRPTW) + +The locations have time windows within which the visits must be made. If transit time matrix has been set using add_transit_time_matrix, then that will be used to validate time windows, else primary matrix is used. Note: - The values provided are considered as units and it is user's responsibility to ensure all time related entries are normalized to one common unit (hours/minutes/seconds/any). - If vehicle locations is not set, then by default 0th index in all columns are considered start and end location for all vehicles. So may be you need to provide big time window for completion of all jobs/depot time window with may be with service time to be 0. + +**Parameters** + +- **`earliest`** (`cudf.Series dtype - int32`) — cudf.Series containing the earliest visit time for each location including the depot. Order is implicit and should be consistent with the data model. +- **`latest`** (`cudf.Series dtype - int32`) — cudf.Series containing the latest visit time for each location including the depot. Order is implicit and should be consistent with the data model. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> cost_mat = [ +... [0, 1, 5, 2], +... [2, 0, 7, 4], +... [1, 5, 0, 9], +... [5, 6, 2, 0] +... ] +>>> cost_mat = cudf.DataFrame(cost_mat) +>>> cost_mat +0 1 2 3 +0 0 1 5 2 +1 2 0 7 4 +2 1 5 0 9 +3 5 6 2 0 +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.add_cost_matrix(cost_mat) +>>> time_mat = [ +... [0, 10, 50, 20], +... [20, 0, 70, 40], +... [10, 50, 0, 90], +... [50, 60, 20, 0] +... ] +>>> time_mat = cudf.DataFrame(time_mat) +>>> data_model.add_transit_time_matrix(time_mat) +>>> earliest = [ 0, 15, 60, 0] # earliest a job can be started +>>> latest = [500, 180, 150, 180] # latest a job can be started +>>> data_model.set_order_time_windows( +... cudf.Series(earliest), +... cudf.Series(latest) +... ) +``` + + +#### def `set_order_prizes` + +```python +set_order_prizes(prizes) +``` + +Set prizes for orders + +**Parameters** + +- **`prizes`** (`cudf.Series dtype - float32`) — cudf.Series containing prizes for each order including the depot (if depot is included in the order list). Order is implicit and should be consistent with the data model. Size of this series must be equal to num_orders in data model. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> prizes = [20, 10, 0, 30] +>>> data_model.set_order_prizes(cudf.Series(prizes)) +``` + + +#### def `set_drop_return_trips` + +```python +set_drop_return_trips(set_drop_return_trips) +``` + +Control if individual vehicles in the fleet return to the end location after the last stop. + +End location is where vehicles will return after completing all the tasks assigned. + +**Parameters** + +- **`set_drop_return_trips`** (`cudf.Series dtype - bool`) — Set True to the drop return trip to end location for each vehicle. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [ 0, 1] +>>> drop_return = [True, False] # Drop the return for the first vehicle +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.set_drop_return_trips(cudf.Series(drop_return)) +``` + + +#### def `set_skip_first_trips` + +```python +set_skip_first_trips(set_skip_first_trips) +``` + +Skips/neglects cost of travel to first task location, implicitly skipping the travel to location. + +**Parameters** + +- **`set_skip_first_trips`** (`cudf.Series dtype - bool`) — Set True to skip the trip cost to first task location. + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [ 0, 1] +>>> skip_first_trip = [False, True] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.set_skip_first_trips(cudf.Series(skip_first_trip)) +``` + + +#### def `add_vehicle_order_match` + +```python +add_vehicle_order_match(vehicle_id, orders) +``` + +Control if a vehicle can only serve a subset of orders + +**Parameters** + +- **`vehicle_id `** (`Integer`) — vehicle id of the vehicle that has restriction on orders +- **`orders `** (`cudf.Series dtype - int32`) — cudf.Series contains the orders that can be fulfilled by vehicle with vehicle_id +- **`Note: A user can set this multiple times. However, if it is`** — set more than once for same vehicle, the order list will be overridden with the most recent function call. The orders in the give list allowed to be served by other vehicles. To make any order served only by a particular vehicle, use add_order_vehicle_match function + +**Examples** + +```python +>>> n_locations = 4 +>>> n_vehicles = 3 +>>> d = routing.DataModel(n_locations, n_vehicles) +>>> distance = [ +>>> [0., 1., 5., 2.], [2., 0., 7., 4.], +>>> [1., 5., 0., 9.], [5., 6., 2., 0.]] +>>> d.add_cost_matrix(cudf.DataFrame(distances)) +>>> # vehicle 0 serves order 1, vehicle 1 serves order 2, +>>> # vehicle 2 serves order 3 +>>> d.add_vehicle_order_match(0, cudf.Series([1])) +>>> d.add_vehicle_order_match(1, cudf.Series([2])) +>>> d.add_vehicle_order_match(2, cudf.Series([3])) +>>> cuopt_solution = routing.Solve(d) +``` + + +#### def `add_order_vehicle_match` + +```python +add_order_vehicle_match(order_id, vehicles) +``` + +Control if an order should only be served by a subset of vehicles + +**Parameters** + +- **`order_id `** (`Integer`) — order id of the order that has restriction on vehicles +- **`vehicles `** (`cudf.Series dtype - int32`) — cudf.Series contains the vehicles that can fulfill the order with order_id +- **`Note: A user can set this multiple times. However, if it is`** — set more than once for same order, the vehicle list will be overridden with the most recent function call The vehicles in the give list can serve other orders as well. To make a vehicle serve only a subset of orders use add_vehicle_order_match function + +**Examples** + +```python +>>> n_locations = 4 +>>> n_vehicles = 3 +>>> d = routing.DataModel(n_locations, n_vehicles) +>>> distance = [ +>>> [0., 1., 5., 2.], [2., 0., 7., 4.], +>>> [1., 5., 0., 9.], [5., 6., 2., 0.]] +>>> d.add_cost_matrix(cudf.DataFrame(distances)) +>>> # order 1 can be served only by vehicle 0, +>>> # order 2 can be served only by vehicle 1, +>>> # order 3 can be served only by vehicle 2 +>>> d.add_order_vehicle_match(1, cudf.Series([0])) +>>> d.add_order_vehicle_match(2, cudf.Series([1])) +>>> d.add_order_vehicle_match(3, cudf.Series([2])) +>>> cuopt_solution = routing.Solve(d) +``` + + +#### def `set_order_service_times` + +```python +set_order_service_times(service_times, vehicle_id = -1) +``` + +In fully heterogeneous fleet mode, vehicle can take different amount of times to complete a task based on their profile and the order being served. Here we enable that ability to the user by setting for each vehicle id the corresponding service times. They can be the same for all orders per vehicle/vehicle type or unique. + +The service times are defaulted for all vehicles unless vehicle id is specified. If no default service times are given then the solver expects all vehicle ids up to fleet size to be specified. + +**Parameters** + +- **`service_times`** (`cudf.Series dtype - int32`) — service times of size number of orders +- **`vehicle_id `** (`int32`) — Vehicle id +- **`Note: A user can set this multiple times. However, if it is`** — set more than once for same vehicle, the service times list will be overridden with the most recent function call + +**Examples** + +```python +>>> n_locations = 4 +>>> n_vehicles = 3 +>>> d = routing.DataModel(n_locations, n_vehicles) +>>> distance = [ +>>> [0., 1., 5., 2.], [2., 0., 7., 4.], +>>> [1., 5., 0., 9.], [5., 6., 2., 0.]] +>>> d.add_cost_matrix(cudf.DataFrame(distances)) +>>> # default for all +>>> d.set_order_service_times(cudf.Series([0, 1, 1, 1])) +>>> # override vehicle 1 +>>> d.set_order_service_times(cudf.Series([0, 2, 4, 5]), 1) +>>> cuopt_solution = routing.Solve(d) +``` + + +#### def `add_capacity_dimension` + +```python +add_capacity_dimension(name, demand, capacity) +``` + +Add capacity dimensions to model the Capacitated Vehicle Routing Problem (CVRP) + +The vehicles have a limited carrying capacity of the goods that must be delivered. This function can be called more than once to model multiple capacity dimensions (weight, volume, number of orders, skills). After solving the problem, the demands on each route will not exceed the vehicle capacities. Note: - If vehicle locations is not set, then by default 0th index in demand column is considered start and end location of all the vehicles. May be it is better to keep demand to be 0. + +**Parameters** + +- **`name`** (`str`) — user-specified name for the dimension +- **`demand`** (`cudf.Series dtype - int32`) — cudf.Series containing integer demand value for each locations, including the depot. Order is implicit and should be consistent with the data model. +- **`capacity`** (`cudf.Series dtype - int32`) — cudf.Series containing integer capacity value for each vehicle in the fleet. Size of this series must be equal to fleet_size in data model + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> demand_weight = [0, 10, 20, 40] +>>> skill_x = [0, 1, 0, 1] # 0 - skill not needed, 1 - needed +>>> skill_y = [0, 0, 1, 1] # 0 - skill not needed, 1 - needed +>>> vehicles = [ 0, 1] +>>> # Vehicle 0 can carry at max 50 units and vehicle 1 100 units +>>> capacity_weight = [50, 100] +>>> # If vehicle has skill keep a high value > number of orders, else 0 +>>> veh_skill_x = [0, 1000] # vehicle-0 doesn't have the skill +>>> veh_skill_y = [1000, 1000] # both vehicles have the skill +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> # Add weight capacity dimension +>>> data_model.add_capacity_dimension( +... "weight", +... cudf.Series(demand_weight), +... cudf.Series(capacity_weight) +... ) +>>> # Add skill x as capacity +>>> data_model.add_capacity_dimension( +... "skill_x", +... cudf.Series(skill_x), +... cudf.Series(veh_skill_x) +... ) +>>> # Add skill y as capacity +>>> data_model.add_capacity_dimension( +... "skill_y", +... cudf.Series(skill_y), +... cudf.Series(veh_skill_y) +... ) +``` + + +#### def `set_vehicle_max_costs` + +```python +set_vehicle_max_costs(vehicle_max_costs) +``` + +Limits per vehicle primary matrix cost accumulated along a route. + +**Parameters** + +- **`vehicle_max_costs`** (`cudf.Series dtype - float32`) — Upper bound per vehicle for max distance cumulated on a route + +**Examples** + +```python +>>> from cuopt import routing +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> vehicle_max_costs = [150, 200] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.set_vehicle_max_costs(cudf.Series(vehicle_max_costs)) +``` + + +#### def `set_vehicle_max_times` + +```python +set_vehicle_max_times(vehicle_max_times) +``` + +Limits per vehicle the time accumulated along a route. This limit accounts for both travel, service and wait time. + +**Parameters** + +- **`vehicle_max_times`** (`cudf.Series dtype - float32`) — Upper bound per vehicle for max duration cumulated on a route + +**Examples** + +```python +>>> from cuopt import routing +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> vehicle_max_times = [150, 200] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.set_vehicle_max_times(cudf.Series(vehicle_max_times)) +``` + + +#### def `set_vehicle_fixed_costs` + +```python +set_vehicle_fixed_costs(vehicle_fixed_costs) +``` + +Limits per vehicle primary matrix cost accumulated along a route. Lets the solver find the optimal fleet according to vehicle costs. In a heterogeneous setting, not all vehicles will have the same cost. Sometimes it may be optimal to use two vehicles with lower cost compared to one vehicle with a huge cost. + +**Parameters** + +- **`vehicle_fixed_costs`** (`cudf.Series dtype - float32`) — Cost of each vehicle + +**Examples** + +```python +>>> from cuopt import routing +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> vehicle_fixed_costs = [16, 1] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.set_vehicle_fixed_costs( +>>> cudf.Series(vehicle_fixed_costs) +>>> ) +``` + + +#### def `set_min_vehicles` + +```python +set_min_vehicles(min_vehicles) +``` + +Request a minimum number of vehicles to be used for routing. Note: The resulting solution may not be optimal. + +**Parameters** + +- **`min_vehicles`** (`Integer`) — The minimum number of vehicle to use. + +**Examples** + +```python +>>> from cuopt import routing +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> # Set minimum vehicles that needs to be used to find the solution +>>> data_model.set_min_vehicles(2) +``` + + +#### def `get_num_locations` + +```python +get_num_locations() +``` + +Returns the number of locations (vehicle start/end locations + task locations). + + +#### def `get_fleet_size` + +```python +get_fleet_size() +``` + +Returns the number of vehicles in the fleet. + + +#### def `get_num_orders` + +```python +get_num_orders() +``` + +Return number of orders. + + +#### def `get_cost_matrix` + +```python +get_cost_matrix(vehicle_type = 0) +``` + +Returns cost matrix as 2D DeviceNDArray in row major format. + + +#### def `get_transit_time_matrix` + +```python +get_transit_time_matrix(vehicle_type = 0) +``` + +Returns transit time matrix as 2D DeviceNDArray in row major format. + + +#### def `get_transit_time_matrices` + +```python +get_transit_time_matrices() +``` + +Returns all transit time matrices as 2D DeviceNDArray in row major format as dictionary with vehicle types as keys. + + +#### def `get_initial_solutions` + +```python +get_initial_solutions() +``` + +#### def `get_order_locations` + +```python +get_order_locations() +``` + +Returns order locations as cudf.Series with int type. + + +#### def `get_vehicle_types` + +```python +get_vehicle_types() +``` + +Returns types of vehicles in the fleet as cudf.Series with uint8 type + + +#### def `get_pickup_delivery_pairs` + +```python +get_pickup_delivery_pairs() +``` + +Returns pick up and delivery order indices as cudf.Series with int type. + + +#### def `get_vehicle_time_windows` + +```python +get_vehicle_time_windows() +``` + +Returns earliest and latest time windows as cudf.Series with int type. + + +#### def `get_vehicle_locations` + +```python +get_vehicle_locations() +``` + +Returns start and return locations as cudf.Series with int type. + + +#### def `get_drop_return_trips` + +```python +get_drop_return_trips() +``` + +Returns drop return trips as cudf.Series with bool type. + + +#### def `get_skip_first_trips` + +```python +get_skip_first_trips() +``` + +Returns skip first trips as cudf.Series with bool type. + + +#### def `get_capacity_dimensions` + +```python +get_capacity_dimensions() +``` + +Returns a dictionary containing demands and capacity under demand name. + + +#### def `get_order_time_windows` + +```python +get_order_time_windows() +``` + +Returns earliest and latest time as cudf.Series with int type. + + +#### def `get_order_prizes` + +```python +get_order_prizes() +``` + +Returns order prizes as cudf.Series with float32 type + + +#### def `get_break_locations` + +```python +get_break_locations() +``` + +Returns break locations as cudf.Series with int type. + + +#### def `get_break_dimensions` + +```python +get_break_dimensions() +``` + +Returns a dictionary containing break earliest, latest, duration under demand name. + + +#### def `get_non_uniform_breaks` + +```python +get_non_uniform_breaks() +``` + +Returns a dictionary containing breaks + + +#### def `get_objective_function` + +```python +get_objective_function() +``` + +Returns objectives as cudf.Series with int type and weights as cudf.Series with float type. + + +#### def `get_vehicle_max_costs` + +```python +get_vehicle_max_costs() +``` + +Returns max costs per vehicles + + +#### def `get_vehicle_max_times` + +```python +get_vehicle_max_times() +``` + +Returns max times per vehicles + + +#### def `get_vehicle_fixed_costs` + +```python +get_vehicle_fixed_costs() +``` + +Returns fixed costs per vehicles + + +#### def `get_vehicle_order_match` + +```python +get_vehicle_order_match() +``` + +Returns a dictionary containing the orders that can be fulfilled for specified vehicles + + +#### def `get_order_vehicle_match` + +```python +get_order_vehicle_match() +``` + +Returns a dictionary containing the vehicles that can fulfill specified orders + + +#### def `get_order_service_times` + +```python +get_order_service_times(vehicle_id = -1) +``` + +Returns a dictionary containing the vehicles and their associated service times + + +#### def `get_min_vehicles` + +```python +get_min_vehicles() +``` + +Returns minimum vehicles set. + + +
+ +### class `SolverSettings` + +Initialize a SolverSettings. + + +#### def `__init__` + +```python +__init__() +``` + +#### def `set_time_limit` + +```python +set_time_limit(seconds) +``` + +Set a fixed solving time in seconds, the timer starts when `Solve` is called. + +Accuracy may be impacted. Problem under 100 locations may be solved with reasonable accuracy under a second. Larger problems may need a few minutes. A generous upper bond is to set the number of seconds to num_locations. By default it is set to num_locations/5. If increased accuracy is desired, this needs to set to higher numbers. + +**Parameters** + +- **`seconds`** (`Float`) — The number of seconds + +**Examples** + +```python +>>> from cuopt import routing +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +........... +>>> settings = routing.SolverSettings() +>>> settings.set_time_limit(0.1) +``` + + +#### def `set_verbose_mode` + +```python +set_verbose_mode(verbose) +``` + +Displaying internal information during the solver execution. + +**Parameters** + +- **`verbose`** (`bool`) — Set True to display information. Execution time may be impacted. + + +#### def `set_error_logging_mode` + +```python +set_error_logging_mode(logging) +``` + +Displaying constraint error information during the solver execution. + +**Parameters** + +- **`logging`** (`bool`) — Set True to display information. Execution time may be impacted. + + +#### def `dump_best_results` + +```python +dump_best_results(file_path, interval) +``` + +Dump best results in a given file as csv, reports in given intervals. + +**Parameters** + +- **`file_path`** (`Absolute path of file to be written`) +- **`interval`** (`Report intervals in seconds`) + +**Examples** + +```python +>>> from cuopt import routing +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +........... +>>> settings = routing.SolverSettings() +>>> settings.dump_best_results("results.csv", 20) +``` + + +#### def `dump_config_file` + +```python +dump_config_file(file_name) +``` + +Dump configuration information in a given file as yaml + +**Parameters** + +- **`file_name`** (`Absolute path of file to be written`) + + +#### def `get_time_limit` + +```python +get_time_limit() +``` + +Returns solving time set. + + +#### def `get_best_results_file_path` + +```python +get_best_results_file_path() +``` + +Returns file path where result will be stored, if not set, it will return None. + + +#### def `get_config_file_name` + +```python +get_config_file_name() +``` + +Returns the full abs path of the file including the filename where the configuration data will be dumped if not set, it will return None + + +#### def `get_best_results_interval` + +```python +get_best_results_interval() +``` + +Returns interval set, if not set, it will return None + + +
+ +### def `Solve` + +```python +Solve(data_model, solver_settings = None) +``` + +Solves the routing problem. + +**Parameters** + +- **`data_model: DataModel`** — data model containing orders, vehicles and constraints. +- **`solver_settings: SolverSettings`** — settings to configure solver configurations. By default, it uses default solver settings to solve. + +**Returns** + +- **`assignment`** (`Assignment`) — Assignment object containing the solver output. + + +
+ +### def `BatchSolve` + +```python +BatchSolve(data_model_list, solver_settings = None) +``` + +Solves multiple routing problems in batch mode using parallel execution. + +**Parameters** + +- **`data_model_list: list of DataModel`** — List of data model objects representing routing problems to solve. +- **`solver_settings: SolverSettings`** — Settings to configure solver configurations. By default, it uses default solver settings to solve. + +**Returns** + +- **``** (`tuple`) — A tuple containing: - list of Assignment: Solutions for each routing problem + +**Examples** + +```python +>>> from cuopt import routing +>>> import cudf +>>> # Create multiple data models +>>> data_models = [] +>>> for i in range(5): +... cost_matrix = cudf.DataFrame([[0, 1, 2], [1, 0, 3], [2, 3, 0]]) +... dm = routing.DataModel(3, 1) +... dm.add_cost_matrix(cost_matrix) +... data_models.append(dm) +>>> settings = routing.SolverSettings() +>>> settings.set_time_limit(1.0) +>>> solutions, solve_time = routing.BatchSolve(data_models, settings) +``` + + + +## Assignment + +
+ +### class `SolutionStatus` + +Status of a routing solve. + +**Attributes** + +- **`SUCCESS`** (`int`) — A valid solution was found. +- **`FAIL`** (`int`) — The solver failed to find a feasible solution. +- **`TIMEOUT`** (`int`) — The solver reached the time limit before finding a solution. +- **`EMPTY`** (`int`) — No orders were provided; the solution is trivially empty. + + +**Members:** `SUCCESS`, `FAIL`, `TIMEOUT`, `EMPTY` + +
+ +### class `Assignment` + +A container of vehicle routing solver output + +**Parameters** + +- **`vehicle_count`** (`Integer`) — Number of vehicles in the solution +- **`total_objective_value`** (`Float64`) — Objective value calculated as per objective functions and weights +- **`objective_values`** (`dict[Objective, Float64]`) +- **`route_df: cudf.DataFrame`** — Contains route, vehicle_id, arrival_stamp. +- **`accepted: cudf.Series`** +- **`status: Integer`** — Solver status 0 - SUCCESS, 1 - FAIL, 2 - TIMEOUT and 3 - EMPTY. +- **`message: String`** — Any error message if there is failure +- **`undeliverable_orders: cudf.Series`** — Orders which can not be served + + +#### def `__init__` + +```python +__init__(vehicle_count, total_objective_value, objective_values, route_df, accepted, status, message, error_status, error_message, undeliverable_orders) +``` + +#### def `get_vehicle_count` + +```python +get_vehicle_count() +``` + +Returns the number of vehicle needed for this routing assignment. + + +#### def `get_total_objective` + +```python +get_total_objective() +``` + +Returns the objective value calculated based on the user provided objective function and the routes found by the solver. + + +#### def `get_objective_values` + +```python +get_objective_values() +``` + +Returns the individual objective_values as dictionary + + +#### def `get_route` + +```python +get_route() +``` + +Returns the route, truck ids for each stop and the arrival stamp as cudf.DataFrame. + +**Examples** + +```python +>>> import cuopt +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> cost_mat = [ +... [0, 1, 5, 2], +... [2, 0, 7, 4], +... [1, 5, 0, 9], +... [5, 6, 2, 0] +... ] +>>> cost_mat = cudf.DataFrame(cost_mat) +>>> cost_mat + 0 1 2 3 +0 0 1 5 2 +1 2 0 7 4 +2 1 5 0 9 +3 5 6 2 0 +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.set_matrix(cost_mat) +>>> solver = cuopt.Solver(data_model) +>>> solver.set_min_vehicles(len(vehicles)) +>>> solver.set_min_vehicles(len(vehicles)) +>>> solution = solver.solve() +>>> solution.get_route() +>>> solution.get_route() + route arrival_stamp truck_id location +0 0 0.0 1 0 +1 3 2.0 1 3 +2 2 4.0 1 2 +3 0 5.0 1 0 +4 0 0.0 0 0 +5 1 1.0 0 1 +6 0 3.0 0 0 +``` + + +#### def `get_status` + +```python +get_status() +``` + +Returns the final solver status as per SolutionStatus. + + +#### def `get_message` + +```python +get_message() +``` + +Returns the final solver message as per SolutionStatus. + + +#### def `get_error_status` + +```python +get_error_status() +``` + +Returns the error status as per ErrorStatus. + + +#### def `get_error_message` + +```python +get_error_message() +``` + +Returns the error message as per ErrorMessage. + + +#### def `get_infeasible_orders` + +```python +get_infeasible_orders() +``` + +Returns the infeasible order numbers as cudf.Series. + + +#### def `get_accepted_solutions` + +```python +get_accepted_solutions() +``` + +#### def `display_routes` + +```python +display_routes() +``` + +Display the solution in human readable format. Intended for relatively small inputs. + +**Examples** + +```python +>>> import cuopt +>>> import cudf +>>> locations = [0, 1, 2, 3] +>>> vehicles = [0, 1] +>>> cost_mat = [ +... [0, 1, 5, 2], +... [2, 0, 7, 4], +... [1, 5, 0, 9], +... [5, 6, 2, 0] +... ] +>>> cost_mat = cudf.DataFrame(cost_mat) +>>> cost_mat + 0 1 2 3 +0 0 1 5 2 +1 2 0 7 4 +2 1 5 0 9 +3 5 6 2 0 +>>> data_model = routing.DataModel(len(locations), len(vehicles)) +>>> data_model.set_matrix(cost_mat) +>>> solver = cuopt.Solver(data_model) +>>> solver.set_min_vehicles(len(vehicles)) +>>> solver.set_min_vehicles(len(vehicles)) +>>> solution = solver.solve() +>>> solution.display_routes() +Vehicle-0 starts at: 0.0, completes at: 3.0, travel time: 3.0, Route : +0->1->0 +Vehicle-1 starts at: 0.0, completes at: 5.0, travel time: 5.0, Route : +0->3->2->0 +This results in a travel time of 8.0 to deliver all routes +``` + + + +## Utils + +
+ +### def `generate_dataset` + +```python +generate_dataset(locations = 100, asymmetric = True, min_demand = cudf.Series(), max_demand = cudf.Series(), min_capacities = cudf.Series(), max_capacities = cudf.Series(), min_service_time = 0, max_service_time = 0, tw_tightness = 0.0, drop_return_trips = 0.0, shifts = 1, n_vehicle_types = 1, n_matrix_types = 1, distribution = utils_wrapper.DatasetDistribution.CLUSTERED, center_box = None, seed = 0) +``` + +Constructs a dataset from the given parameters. locations: + +**Parameters** + +- **`locations: int32`** — Number of locations to visit, including the depot. +- **`asymmetric`** (`bool`) — Specifies if the matrices (both cost and time) should be asymmetric. +- **`min_demand`** (`cudf.Series`) — Series will be cast to int32. Minimum demand value along each dimension. +- **`max_demand`** (`cudf.Series`) — Series will be cast to int32. Maximum demand value along each dimension. +- **`min_capacities`** (`cudf.Series`) — Series will be cast to int32. Minimum capacity value along each dimension. +- **`max_capacities`** (`cudf.Series`) — Series will be cast to int32. Maximum capacity value along each dimension. +- **`min_service_time`** (`int32`) — Lower bound for generated service time. +- **`max_service_time`** (`int32`) — Upper bound for generated service time. +- **`tw_tightness`** (`float32`) — Can make a problem more or less difficult to solve. Feasibility is ensured because we made sure the latest time of a node is larger than its depot-node distance. +- **`drop_return_trips`** (`float32`) — Percentage of vehicles in the fleet that shouldn't return to the depot. +- **`shifts`** (`int32`) — The number of shifts in the dataset. This will create vehicle time windows that split the fleet into multiple shifts. +- **`n_vehicle_types`** (`int32`) — Multiple vehicle types can be generated each with a corresponding cost matrix or time matrix. +- **`n_matrix_types`** (`int32`) — There can be one cost and time matrix per vehicle type. The cupy array generated is of size dim4(n_vehicle_types, n_matrix_types, nloc, nloc). In the case the n_matrix_types is 1, the cost matrix is used for time. +- **`distribution`** (`DatasetDistribution`) — The distribution of datapoints similar to what is done for the homberger cvrptw dataset. https://www.sintef.no/projectweb/top/vrptw/homberger-benchmark/ Clustered datasets are usually faster to generate and solve. +- **`center_box`** (`tuple`) — The bounding box which constrains all the centroids. +- **`seed`** (`int32`) — Random seed for the raft generator. + +**Returns** + +- **`pos_list`** (`cudf.DataFrame`) — x and y coordinates. +- **`matrices`** (`cupy.ndarray`) — Vehicle cost and time matrices as dim4(n_vehicle_types, n_matrix_types, nloc, nloc). +- **`orders`** (`cudf.DataFrame`) — Time windows and multi dimension demand for each location. +- **`vehicles`** (`cudf.DataFrame`) — Time windows and multi dimension capacity for each vehicle. + + +
+ +### def `update_routes_and_vehicles` + +```python +update_routes_and_vehicles(output_df, route, order_pdf, order_data, vehicle_constraints) +``` + +Helper function to update the final routes and vehicle time windows when running cuopt in batches. Routes are updated in output_df and vehicle time windows are updated in vehicle_constraints. + + +
+ +### def `add_vehicle_constraints` + +```python +add_vehicle_constraints(num_vehicles, vehicle_capacity, break_earliest = None, break_latest = None, vehicle_speed = 1) +``` + +Helper function to add vehicle constraints when running cuopt in batches. Vehicle constraints that can be added include number of vehicles, vehicle capacity, speed of vehicles and earliest and latest vehicle break times. + + +
+ +### def `create_pickup_delivery_data` + +```python +create_pickup_delivery_data(matrix_pdf, raw_order_pdf, depot, vehicle_constraints) +``` + +Creates and returns travel matrix, order information and vehicle information for pickup and deliveries. + + +
+ +### class `DatasetDistribution` + +**Members:** `CLUSTERED`, `RANDOM`, `RANDOM_CLUSTERED` + +
+ +### class `ErrorStatus` + +**Members:** `Success`, `ValidationError`, `OutOfMemoryError`, `RuntimeError` + +
+ +### class `Objective` + +Enums to configure objective of the solution + +COST - Models with respect to total cost TRAVEL_TIME - Models with respect to travel time (This includes drive, service and wait time) # noqa VARIANCE_ROUTE_SIZE - Models with respect to dissimilarity of route sizes It computes the L2 variance (squared) in the number of orders served by each route. VARIANCE_ROUTE_SERVICE_TIME - Models with respect to disssimilarty of route service times It computes L2 variance (squared) of the accumulated service times of of each route PRIZE - Models with respect to prizes collected by the serviced orders VEHICLE_FIXED_COST - Models cost per vehicle. Enabled when set_vehicle_fixed_costs is used. + + +**Members:** `COST`, `TRAVEL_TIME`, `VARIANCE_ROUTE_SIZE`, `VARIANCE_ROUTE_SERVICE_TIME`, `PRIZE`, `VEHICLE_FIXED_COST` + +
+ +### class `NodeType` + +Types for nodes/route returned by the solver + +DEPOT - Indicates whether a given node is depot PICKUP - Indicates whether a given node corresponds to a pickup DELIVERY - Indicates whether a given node corresponds to a delivery BREAK - Indicates whether a given node corresponds to a break + + +**Members:** `DEPOT`, `PICKUP`, `DELIVERY`, `BREAK` diff --git a/fern/docs/pages/cuopt-python/routing/routing-examples.mdx b/fern/docs/pages/cuopt-python/routing/routing-examples.mdx new file mode 100644 index 0000000000..09c84a6ede --- /dev/null +++ b/fern/docs/pages/cuopt-python/routing/routing-examples.mdx @@ -0,0 +1,314 @@ +--- +title: "Routing Examples" +--- + +This section contains examples for the cuOpt routing Python API. + +## Intra-factory Transport + +A capacitated pickup-and-delivery problem with time windows (PDPTW) for a fleet of autonomous mobile robots (AMRs) moving parts between processing stations on a factory floor. The example uses `cuopt.distance_engine.WaypointMatrix` to derive a cost matrix from a weighted waypoint graph, sets up pickup/delivery orders with demand and time windows, solves with `cuopt.routing.Solve`, and expands the target-location route back to a waypoint-level route per robot. + +![Waypoint graph](/docs/images/waypoint_graph.png) + +**Problem details:** + + - 4 target locations: 1 start location for AMRs and 3 processing stations + - 6 transport orders (pickup/delivery pairs) with individual time windows + - 2 AMRs, each with a carrying capacity of 2 parts + - Factory hours: 0 to 100 time units + +{/* embed: examples/intra_factory_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Intra-factory transport example. +# +# Scenario: a small factory floor where a fleet of autonomous mobile robots +# (AMRs) pick up parts at processing stations and deliver them to other +# stations (or off the floor). cuOpt plans the routes for each robot such +# that every order is served within its pickup/delivery time windows and +# no robot exceeds its carrying capacity. +# +# This is a Capacitated Pickup-and-Delivery Problem with Time Windows +# (PDPTW) solved on a weighted waypoint graph. + +import cudf +import numpy as np + +from cuopt import distance_engine, routing + +# --- Factory layout -------------------------------------------------------- +# Waypoints in the factory, referenced by integer id: +# 0 = AMR depot (robots start/return here) +# 4 = Station A +# 5 = Station B +# 6 = Station C +# Other waypoints (1, 2, 3, 7, 8, 9) are intermediate nodes that robots +# travel through but never stop at. +DEPOT = 0 +STATION_A = 4 +STATION_B = 5 +STATION_C = 6 +TARGET_LOCATIONS = np.array([DEPOT, STATION_A, STATION_B, STATION_C]) + +# Factory operating hours (in time units). +FACTORY_OPEN = 0 +FACTORY_CLOSE = 100 + +# Weighted waypoint graph of the factory floor in Compressed Sparse Row +# (CSR) form: for node i, GRAPH_OFFSETS[i]:GRAPH_OFFSETS[i+1] indexes into +# GRAPH_EDGES (destination nodes) and GRAPH_WEIGHTS (edge costs). See the +# `cuopt.distance_engine.WaypointMatrix` API reference in the cuOpt User +# Guide for input requirements. +GRAPH_OFFSETS = np.array([0, 1, 3, 7, 9, 11, 13, 15, 17, 20, 22]) +GRAPH_EDGES = np.array( + [2, 2, 4, 0, 1, 3, 5, 2, 6, 1, 7, 2, 8, 3, 9, 4, 8, 5, 7, 9, 6, 8] +) +GRAPH_WEIGHTS = np.array( + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2] +) + + +def build_cost_matrix(): + """Compute an all-pairs cost matrix over the target locations.""" + graph = distance_engine.WaypointMatrix( + GRAPH_OFFSETS, GRAPH_EDGES, GRAPH_WEIGHTS + ) + cost_matrix = graph.compute_cost_matrix(TARGET_LOCATIONS) + # waypoint id -> cost-matrix index (e.g. Station A [waypoint 4] -> index 1) + wp_to_idx = {int(wp): i for i, wp in enumerate(TARGET_LOCATIONS)} + return graph, cost_matrix, wp_to_idx + + +def build_orders(): + """Six transport orders. Each row is one pickup/delivery pair.""" + return cudf.DataFrame( + { + "pickup_location": [ + STATION_A, + STATION_B, + STATION_C, + STATION_C, + STATION_B, + STATION_A, + ], + "delivery_location": [ + STATION_B, + STATION_C, + DEPOT, + STATION_B, + STATION_A, + DEPOT, + ], + "demand": [1, 1, 1, 1, 1, 1], + "earliest_pickup": [0, 0, 0, 0, 0, 0], + "latest_pickup": [10, 20, 30, 10, 20, 30], + "earliest_delivery": [0, 0, 0, 0, 0, 0], + "latest_delivery": [45, 45, 45, 45, 45, 45], + "pickup_service_time": [2, 2, 2, 2, 2, 2], + "delivery_service_time": [2, 2, 2, 2, 2, 2], + } + ) + + +def build_fleet(): + """Two AMRs, each able to carry two parts at once.""" + return cudf.DataFrame({"robot_id": [0, 1], "capacity": [2, 2]}).set_index( + "robot_id" + ) + + +def build_data_model(cost_matrix, orders, fleet, wp_to_idx): + """Assemble the cuOpt routing DataModel from the problem inputs.""" + n_locations = len(cost_matrix) + n_vehicles = len(fleet) + # Each order contributes two stops: one pickup and one delivery. + n_orders = len(orders) * 2 + + data_model = routing.DataModel(n_locations, n_vehicles, n_orders) + data_model.add_cost_matrix(cost_matrix) + + # Capacity: pickups add load, deliveries remove it. + demand = cudf.concat( + [orders["demand"], -orders["demand"]], ignore_index=True + ) + data_model.add_capacity_dimension("parts", demand, fleet["capacity"]) + + # Order locations are expressed as cost-matrix indices, not waypoint ids. + pickup_idx = orders["pickup_location"].map(wp_to_idx) + delivery_idx = orders["delivery_location"].map(wp_to_idx) + data_model.set_order_locations( + cudf.concat([pickup_idx, delivery_idx], ignore_index=True) + ) + + # Pickup at row i must be served before its delivery at row i + n. + n = len(orders) + data_model.set_pickup_delivery_pairs( + cudf.Series(range(n)), cudf.Series(range(n, 2 * n)) + ) + + # Time windows. + data_model.set_order_time_windows( + cudf.concat( + [orders["earliest_pickup"], orders["earliest_delivery"]], + ignore_index=True, + ), + cudf.concat( + [orders["latest_pickup"], orders["latest_delivery"]], + ignore_index=True, + ), + ) + data_model.set_order_service_times( + cudf.concat( + [orders["pickup_service_time"], orders["delivery_service_time"]], + ignore_index=True, + ) + ) + data_model.set_vehicle_time_windows( + cudf.Series([FACTORY_OPEN] * n_vehicles), + cudf.Series([FACTORY_CLOSE] * n_vehicles), + ) + return data_model + + +def print_schedule(solution, graph, wp_to_idx): + """Print a per-robot, human-readable schedule of stops and waypoint paths.""" + idx_to_wp = {i: wp for wp, i in wp_to_idx.items()} + route = solution.get_route() + + print(f"\nTotal route cost: {solution.get_total_objective():g}") + print(f"Robots used: {solution.get_vehicle_count()}\n") + + for robot_id in sorted(route["truck_id"].unique().to_arrow().to_pylist()): + stops_gpu = route[route["truck_id"] == robot_id] + stops = stops_gpu.to_pandas() + + print(f"Robot {robot_id}:") + for _, s in stops.iterrows(): + print( + f" t={s['arrival_stamp']:>5g} " + f"waypoint {idx_to_wp[s['location']]:<2} {s['type']}" + ) + + # compute_waypoint_sequence mutates its input, so hand it a fresh copy. + wp_path = graph.compute_waypoint_sequence( + TARGET_LOCATIONS, stops_gpu.copy() + ) + path_str = " -> ".join( + str(w) for w in wp_path["waypoint_sequence"].to_arrow().to_pylist() + ) + print(f" path: {path_str}\n") + + +def main(): + graph, cost_matrix, wp_to_idx = build_cost_matrix() + orders = build_orders() + fleet = build_fleet() + + print("Target locations (waypoint -> cost-matrix index):", wp_to_idx) + print("\nCost matrix between target locations:") + print(cost_matrix) + print(f"\n{len(orders)} transport orders, {len(fleet)} AMRs.") + + data_model = build_data_model(cost_matrix, orders, fleet, wp_to_idx) + + settings = routing.SolverSettings() + settings.set_time_limit(5) + + solution = routing.Solve(data_model, settings) + if solution.get_status() != 0: + print( + f"cuOpt failed to find a solution (status={solution.get_status()})" + ) + return + + print_schedule(solution, graph, wp_to_idx) + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +## TSP Batch Mode + +The routing Python API supports **batch mode** for solving many TSP (or routing) instances in a single call. Instead of calling `cuopt.routing.Solve` repeatedly, you build a list of `cuopt.routing.DataModel` objects and call `cuopt.routing.BatchSolve`. The solver runs the problems in parallel to improve throughput. + +**When to use batch mode:** + + - You have **many similar routing problems** (e.g., dozens or hundreds of small TSPs). + - You want to **maximize throughput** by utilizing the GPU across multiple problems at once. + - Problem sizes and structure are compatible with the same `cuopt.routing.SolverSettings` (e.g., same time limit). + +**Returns:** A list of `cuopt.routing.Assignment` objects, one per input data model, in the same order as `data_model_list`. Use `cuopt.routing.Assignment.get_status` and other assignment methods to inspect each solution. + +The following example builds several TSPs of different sizes, solves them in one batch, and prints a short summary per solution. + +{/* embed: examples/tsp_batch_example.py */} +```python +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# TSP batch solve example: solve multiple TSP instances in one call for higher throughput. +# Use this when you have many similar routing problems (e.g., many small TSPs) to solve. + +import cudf +import numpy as np + +from cuopt import routing + + +def create_tsp_cost_matrix(n_locations): + """Create a simple symmetric cost matrix for a TSP of size n_locations.""" + cost_matrix = np.zeros((n_locations, n_locations), dtype=np.float32) + for i in range(n_locations): + for j in range(n_locations): + cost_matrix[i, j] = abs(i - j) + return cudf.DataFrame(cost_matrix) + + +def main(): + # Define multiple TSP sizes to solve in one batch + tsp_sizes = [5, 8, 10, 6, 7, 9] + + # Build one DataModel per TSP + data_models = [] + for n_locations in tsp_sizes: + cost_matrix = create_tsp_cost_matrix(n_locations) + dm = routing.DataModel(n_locations, 1) # n_locations, 1 vehicle (TSP) + dm.add_cost_matrix(cost_matrix) + data_models.append(dm) + + # Shared solver settings for the batch + settings = routing.SolverSettings() + settings.set_time_limit(5.0) + + # Solve all TSPs in batch (parallel execution) + solutions = routing.BatchSolve(data_models, settings) + + # Inspect results + print(f"Solved {len(solutions)} TSPs in batch.") + for i, (size, solution) in enumerate(zip(tsp_sizes, solutions)): + status = solution.get_status() + status_str = ( + "SUCCESS" if status == routing.SolutionStatus.SUCCESS else status + ) + vehicle_count = solution.get_vehicle_count() + print( + f" TSP {i} (size {size}): status={status_str}, vehicles={vehicle_count}" + ) + + +if __name__ == "__main__": + main() +``` +{/* /embed */} + +Sample output: + +**Notes:** + + - All problems in the batch use the **same** `cuopt.routing.SolverSettings` (e.g., time limit, solver options). + - Callbacks are not supported in batch mode. + - For best practices when batching many instances, see the *Add best practices for batch solving* note in the release documentation. diff --git a/fern/docs/pages/cuopt-server/client-api/index.mdx b/fern/docs/pages/cuopt-server/client-api/index.mdx new file mode 100644 index 0000000000..6218d936f2 --- /dev/null +++ b/fern/docs/pages/cuopt-server/client-api/index.mdx @@ -0,0 +1,5 @@ +--- +title: "Client API Overview" +--- + +Client is used to communicate with the cuOpt server. More details can be found below. diff --git a/fern/docs/pages/cuopt-server/client-api/sh-cli-api.mdx b/fern/docs/pages/cuopt-server/client-api/sh-cli-api.mdx new file mode 100644 index 0000000000..ef4f92c3e8 --- /dev/null +++ b/fern/docs/pages/cuopt-server/client-api/sh-cli-api.mdx @@ -0,0 +1,5 @@ +--- +title: "Self-Hosted Service Client API Reference" +--- + +Install `cuopt-sh-client` to access the thin client API. See [Build Your Own Client](sh-cli-build). diff --git a/fern/docs/pages/cuopt-server/client-api/sh-cli-build.mdx b/fern/docs/pages/cuopt-server/client-api/sh-cli-build.mdx new file mode 100644 index 0000000000..586163c187 --- /dev/null +++ b/fern/docs/pages/cuopt-server/client-api/sh-cli-build.mdx @@ -0,0 +1,51 @@ +--- +title: "Build Your Own Self-Hosted Thin Client" +--- + +## Overview + +The thin client and CLI provide ease of access to the cuOpt service, but these are just references on how to communicate with the cuOpt service. Underlying details of the API have been discussed below if you want to create your own thin client. + + +Please don't use `cuopt_self_host_client.py` or `cuopt_sh_client.py` as your thin client name, just to avoid confusion in case if you had installed cuopt thin client from pypi. + + +## Invoking cuOpt Service + +### Sending Request + +The `requestBody` contains a JSON object which will be processed by cuOpt. `/cuopt/request` endpoint is used to send the request. + +Run server with following command in a terminal: + +Here we are sharing current directory as both data and result directory. + +Example with JSON data as a direct string: + +Example with JSON data as a file: + +Success Response: + +When sending files to the server, the server must be configured with appropriate data and result directories to temporarily store these files. These directories can be set using the `-d` and `-r` options when starting the server. Please refer to the [Server CLI documentation](../server-api/server-cli) for more details on configuring these directories. + +`JSON_DATA` should follow the [spec under "POST /cuopt/request" schema](../../open-api) described for cuOpt input. + +## Polling for Request Status: + +The cuOpt service employs an asynchronous interface for invocation and result retrieval. When you make an invocation request, the system will submit your problem to the solver and return a request id. + +Users can poll the request id for status with the help of `/cuopt/request/{request-id}` endpoint. + +In case the solver has completed the job, the response will be "completed". + +Please refer to the [Solver status in spec using "GET /cuopt/request/\{request-id\}"](../../open-api) for more details on responses. + +## cuOpt Result Retrieval + +Once you have received successful response from solver with status "completed", you can retrieve the result with the help of `/cuopt/solution/{request-id}` endpoint. + +This would fetch the result in JSON format. Please refer to the [Response structure in spec using "GET /cuopt/solution/\{request-id\}"](../../open-api) for more details on responses. + + +It is user's responsibility to delete the request and solution files from the data and result directories respectively after retrieving the result. Please refer to the API spec for more details on deletion. + diff --git a/fern/docs/pages/cuopt-server/csp-guides/csp-aws.mdx b/fern/docs/pages/cuopt-server/csp-guides/csp-aws.mdx new file mode 100644 index 0000000000..2529f3ff0a --- /dev/null +++ b/fern/docs/pages/cuopt-server/csp-guides/csp-aws.mdx @@ -0,0 +1,43 @@ +--- +title: "AWS Quickstart Guide" +--- + +By the end of this guide, you will have registered for NVIDIA AI Enterprise, created a Virtual Machine (VM) on Amazon Web Services (AWS) that uses the NVIDIA AI Enterprise image, and run a sample cuOpt workload. This will prepare you to run cuOpt on an AWS VM. + +## Step 1: Create an AWS VM with NVAIE Image + +1. Log in to AWS and navigate to AWS Marketplace. + +2. Look up NVIDIA AI Enterprise in the search bar. If you already have a subscription, NVIDIA AI Enterprise may automatically show in your subscriptions. Otherwise, search for **NVIDIA AI Enterprise** in the search bar. + + ![image](/docs/images/aws1.png) + +3. Click on the **NVIDIA AI Enterprise** subscription. + +4. The following page will open. Click **Launch new instance**. + + ![image](/docs/images/aws2.png) + +5. Configure your VM. If you have deployed a VM through AWS in the past, you should already have an SSH key you can reuse. If this is your first time, create a new SSH key. + + +When selecting the hardware configuration, make sure to select a GPU of A100 or higher. + + + ![image](/docs/images/aws3.png) + +6. After configuring your VM, wait for it to deploy. This might take a few minutes to complete. + +7. Upon deployment completion, you will see the following screen. Select **Connect to your instance**. + + ![image](/docs/images/aws4.png) + +8. Once you have the IP address of your VM, you can connect to it through the AWS SSH portal, or through your terminal. + +## Step 2: Activate NVAIE Subscription + +Once connected to the VM, generate an identity token. Activate your NVIDIA AI Enterprise subscription using that identity token on NGC. Follow the instructions [here](https://docs.nvidia.com/ai-enterprise/deployment/cloud/latest/aws-ai-enterprise-vmi.html#accessing-the-ngc-catalog-on-ngc). + +## Step 3: Run cuOpt + +To run cuOpt, you will need to log in to the NVIDIA Container Registry, pull the cuOpt container, and then run it. To test that it is successfully running, you can run a sample cuOpt request. This process is the same for deploying cuOpt on your own infrastructure. Refer to [Self-Hosted Service Quickstart Guide](#container-from-nvidia-ngc). diff --git a/fern/docs/pages/cuopt-server/csp-guides/csp-azure.mdx b/fern/docs/pages/cuopt-server/csp-guides/csp-azure.mdx new file mode 100644 index 0000000000..427d9e2bf0 --- /dev/null +++ b/fern/docs/pages/cuopt-server/csp-guides/csp-azure.mdx @@ -0,0 +1,56 @@ +--- +title: "Azure Quickstart Guide" +--- + +By the end of this guide, you will have registered for NVIDIA AI Enterprise, created a Virtual Machine (VM) on Microsoft Azure that uses the NVIDIA AI Enterprise image, run a sample cuOpt workload, and you will be ready to run an instance of cuOpt on an Azure VM. To complement this guide, you can watch the video tutorial [here](https://www.youtube.com/watch?v=W7-jMYp58rc). The video tutorial and this user guide cover the same deployment steps. + +## Step 1: Create an Azure VM with NVAIE Image + +1. Log in to Azure and navigate to Azure Marketplace. + +2. Look up **NVIDIA AI Enterprise** in the search bar. + + ![image](/docs/images/azure1.png) + +3. You should see the following results. Click on the first option, the **NVIDIA AI Enterprise** Virtual Machine. + + ![image](/docs/images/azure2.png) + +4. The following page will open. Click **Create**. + + ![image](/docs/images/azure3.png) + +5. Configure your VM. If you have deployed a VM through Azure in the past, you should already have an SSH key you can reuse. If this is your first time, create a new SSH key. + + +When selecting the hardware configuration, make sure to select a GPU of A100 or higher. + + +6. After configuring your VM, wait for it to deploy. This might take a few minutes to complete. + +7. Upon deployment completion, you will see the following screen. Select **Go to resource**. + + ![image](/docs/images/azure4.png) + +8. This will take you to the Overview page of your VM. Select **Connect** at the top of the page. + + ![image](/docs/images/azure5.png) + +9. You can connect to the VM through Azure CLI or through your terminal (Native SSH). The public IP address of your VM is written at the top of the page. + + ![image](/docs/images/azure6.png) + +## Step 2: Activate NVAIE Subscription + +Once connected to the VM, generate an identity token. Activate your NVIDIA AI Enterprise subscription using that identity token on NGC. Follow the instructions [here](https://docs.nvidia.com/ai-enterprise/deployment/cloud/latest/azure-ai-enterprise-vmi.html#accessing-the-ngc-catalog-on-ngc). + +## Step 3: Run cuOpt + +To run cuOpt, you will need to log in to the NVIDIA Container Registry, pull the cuOpt container, and then run it. To test that it is successfully running, you can run a sample cuOpt request. This process is the same for deploying cuOpt on your own infrastructure. Refer [Self-Hosted Service Quickstart Guide](#container-from-nvidia-ngc). + +## Step 4: Mapping Visualization with Azure + +Upon running your optimization problems and getting the optimized routes from cuOpt, you may wish to post-process your data and visualize your data on a map. One tool you can use for this is Azure Maps. Linked below are a demo application and sample code you can use as a reference. + + - [Demo application](https://samples.azuremaps.com/rest-services/mio) + - [Sample code](https://github.com/Azure-Samples/AzureMapsCodeSamples/blob/main/Samples/REST%20Services/MIO/mio.html) diff --git a/fern/docs/pages/cuopt-server/csp-guides/index.mdx b/fern/docs/pages/cuopt-server/csp-guides/index.mdx new file mode 100644 index 0000000000..a67dcd59e1 --- /dev/null +++ b/fern/docs/pages/cuopt-server/csp-guides/index.mdx @@ -0,0 +1,5 @@ +--- +title: "CSP Guides" +--- + +This section contains guides on how to use the cuOpt server in different cloud providers. diff --git a/fern/docs/pages/cuopt-server/examples/index.mdx b/fern/docs/pages/cuopt-server/examples/index.mdx new file mode 100644 index 0000000000..5890e33502 --- /dev/null +++ b/fern/docs/pages/cuopt-server/examples/index.mdx @@ -0,0 +1,5 @@ +--- +title: "Examples" +--- + +This section contains examples on how to use the cuOpt server using the client. diff --git a/fern/docs/pages/cuopt-server/examples/lp-examples.mdx b/fern/docs/pages/cuopt-server/examples/lp-examples.mdx new file mode 100644 index 0000000000..38d0d59ef2 --- /dev/null +++ b/fern/docs/pages/cuopt-server/examples/lp-examples.mdx @@ -0,0 +1,107 @@ +--- +title: "LP Python Examples" +--- + +The following example showcases how to use the `CuOptServiceSelfHostClient` to solve a simple LP problem in normal mode and batch mode (where multiple problems are solved at once). + + +LP batch mode is deprecated and will be removed in a future release. Prefer sequential `cuopt.linear_programming.Solve` calls. Server and CLI batch paths still use batch solve today; check response `warnings` or logs for the deprecation notice. + + +The OpenAPI specification for the server is available in [open-api spec](../../open-api). The example data is structured as per the OpenAPI specification for the server, please refer [LPData under "POST /cuopt/request"](../../open-api) under schema section. LP and MILP share same spec. + +If you want to run server locally, please run the following command in a terminal or tmux session so you can test examples in another terminal. + +## Genric Example With Normal Mode and Batch Mode + +[basic\_lp\_example.py](lp/examples/basic_lp_example.py) + +The response would be as follows: + +Normal mode response: + +Batch mode response: + + +Warm start is only applicable to LP and not for MILP. + + +## Warm Start + +Previously run solutions can be saved and be used as warm start for new requests using previously run reqIds as follows: + +[warmstart\_example.py](lp/examples/warmstart_example.py) + +The response would be as follows: + +## Using MPS or LP file directly + +The self-hosted client accepts both MPS and LP format files — the client dispatches on the file extension (`.lp` ⇒ LP parser, otherwise MPS) and sends the parsed data model to the server. An example on using .mps files as input is shown below: + +[mps\_file\_example.py](lp/examples/mps_file_example.py) + +The response is: + +## Generate Datamodel using Problem File Parser + +Use a `~cuopt.linear_programming.data_model.DataModel` built with `~cuopt.linear_programming.io.Read` as input to `get_LP_solve`; the client dispatches on the file extension (`.mps` / `.qps` vs `.lp`, including `.gz` / `.bz2` compressed variants). For solver settings see [convex optimization parameters](../../convex-settings) and [MIP parameters](../../mip-settings). + +### MPS format + +[mps\_datamodel\_example.py](lp/examples/mps_datamodel_example.py) + +### LP format + +[lp\_datamodel\_example.py](lp/examples/lp_datamodel_example.py) + +### Expected output (either example, same problem instance) + +Example with DataModel is available in the [Examples Notebooks Repository](https://github.com/NVIDIA/cuopt-examples). + +The `data` argument to `get_LP_solve` may be a dictionary of the format shown in [LP Open-API spec](../../open-api). More details on the response can be found under the responses schema ["get /cuopt/request" and "get /cuopt/solution" API spec](../../open-api). + +## Aborting a Running Job in Thin Client + +Please refer to the [aborting-thin-client](#aborting-thin-client) in the MILP Example for more details. + +# LP CLI Examples + +## Generic Example + +The following examples showcase how to use the `cuopt_sh` CLI to solve a simple LP problem. + +Invoke the CLI. + +Response is as follows: + +## Warm Start in CLI + +To use a previous solution as the initial/warm start solution for a new request ID, you are required to save the previous solution, which can be accomplished use option `-k`. Use the previous reqId in the next request as follows: + + +Warm start is only applicable to LP and not for MILP. + + +In case the user needs to update solver settings through CLI, the option `-ss` can be used as follows: + +In the case of batch mode, you can send a bunch of `mps` files at once, and acquire results. The batch mode works only for `mps` in the case of CLI: + + +LP batch mode is deprecated; see [Batch Mode](#batch-mode) in [Convex Features](../../convex-features). + + + +Batch mode is not available for MILP problems. + + +A sample MPS file ([sample.mps](lp/examples/sample.mps)): + +Run the example: + +## Aborting a Running Job In CLI + +Please refer to the [aborting-cli](#aborting-cli) in the MILP Example for more details. + + +Please use solver settings while using .mps files. + diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.py b/fern/docs/pages/cuopt-server/examples/lp/examples/basic_lp_example.py similarity index 97% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.py rename to fern/docs/pages/cuopt-server/examples/lp/examples/basic_lp_example.py index 267ae357bd..04666996b2 100644 --- a/docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.py +++ b/fern/docs/pages/cuopt-server/examples/lp/examples/basic_lp_example.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ Basic LP Server Example (Normal and Batch Mode) diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.sh b/fern/docs/pages/cuopt-server/examples/lp/examples/basic_lp_example.sh old mode 100755 new mode 100644 similarity index 92% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.sh rename to fern/docs/pages/cuopt-server/examples/lp/examples/basic_lp_example.sh index 3e90f3be5f..4253c6f26f --- a/docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.sh +++ b/fern/docs/pages/cuopt-server/examples/lp/examples/basic_lp_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Basic LP CLI Example diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/batch_mode_example.sh b/fern/docs/pages/cuopt-server/examples/lp/examples/batch_mode_example.sh old mode 100755 new mode 100644 similarity index 93% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/batch_mode_example.sh rename to fern/docs/pages/cuopt-server/examples/lp/examples/batch_mode_example.sh index c3b884dc69..53f08b1110 --- a/docs/cuopt/source/cuopt-server/examples/lp/examples/batch_mode_example.sh +++ b/fern/docs/pages/cuopt-server/examples/lp/examples/batch_mode_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # LP Batch Mode CLI Example diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/lp_datamodel_example.py b/fern/docs/pages/cuopt-server/examples/lp/examples/lp_datamodel_example.py similarity index 100% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/lp_datamodel_example.py rename to fern/docs/pages/cuopt-server/examples/lp/examples/lp_datamodel_example.py diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/mps_datamodel_example.py b/fern/docs/pages/cuopt-server/examples/lp/examples/mps_datamodel_example.py similarity index 100% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/mps_datamodel_example.py rename to fern/docs/pages/cuopt-server/examples/lp/examples/mps_datamodel_example.py diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/mps_file_example.py b/fern/docs/pages/cuopt-server/examples/lp/examples/mps_file_example.py similarity index 95% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/mps_file_example.py rename to fern/docs/pages/cuopt-server/examples/lp/examples/mps_file_example.py index dba5634d18..abd34571c1 100644 --- a/docs/cuopt/source/cuopt-server/examples/lp/examples/mps_file_example.py +++ b/fern/docs/pages/cuopt-server/examples/lp/examples/mps_file_example.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ LP MPS File Server Example diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/sample.mps b/fern/docs/pages/cuopt-server/examples/lp/examples/sample.mps similarity index 100% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/sample.mps rename to fern/docs/pages/cuopt-server/examples/lp/examples/sample.mps diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/warmstart_example.py b/fern/docs/pages/cuopt-server/examples/lp/examples/warmstart_example.py similarity index 100% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/warmstart_example.py rename to fern/docs/pages/cuopt-server/examples/lp/examples/warmstart_example.py diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/warmstart_example.sh b/fern/docs/pages/cuopt-server/examples/lp/examples/warmstart_example.sh old mode 100755 new mode 100644 similarity index 100% rename from docs/cuopt/source/cuopt-server/examples/lp/examples/warmstart_example.sh rename to fern/docs/pages/cuopt-server/examples/lp/examples/warmstart_example.sh diff --git a/fern/docs/pages/cuopt-server/examples/milp-examples.mdx b/fern/docs/pages/cuopt-server/examples/milp-examples.mdx new file mode 100644 index 0000000000..a66c40de82 --- /dev/null +++ b/fern/docs/pages/cuopt-server/examples/milp-examples.mdx @@ -0,0 +1,53 @@ +--- +title: "MILP Python Examples" +--- + +The major difference between this example and the LP example is that some of the variables are integers, so `variable_types` need to be shared. The OpenAPI specification for the server is available in [open-api spec](../../open-api). The example data is structured as per the OpenAPI specification for the server, please refer [LPData data under "POST /cuopt/request"](../../open-api) under schema section. LP and MILP share same spec. + +## Generic Example + +[basic\_milp\_example.py](milp/examples/basic_milp_example.py) + +The response would be as follows: + +## Incumbent and Logging Callback + +The incumbent solution can be retrieved using a callback function as follows: + + +Incumbent solution callback is only applicable to MILP. The callback bound can be `None` when the solver has found an incumbent but no finite global bound is available yet. + + +[incumbent\_callback\_example.py](milp/examples/incumbent_callback_example.py) + +Log the callback response: + +Incumbent callback response: + +An example with DataModel is available in the [Examples Notebooks Repository](https://github.com/NVIDIA/cuopt-examples). + +The `data` argument to `get_LP_solve` may be a dictionary of the format shown in [MILP Open-API spec](../../open-api). More details on the response can be found under responses schema in ["/cuopt/request" and "/cuopt/solution" API spec](../../open-api). They can be of different format as well, please check the documentation. + +## Aborting a Running Job in Thin Client + +[abort\_job\_example.py](milp/examples/abort_job_example.py) + +# MILP CLI Examples + +## Generic MILP Example + +The only difference between this example and the prior LP example would be the variable types provided in data. + +Invoke the CLI: + +In case the user needs to update solver settings through CLI, the option `-ss` can be used as follows: + + +Batch mode is not supported for MILP. + + +## Aborting a Running Job In CLI + +UUID that is returned by the solver while the solver is trying to find a solution so users can come back and check the status or query for results. + +This aborts a job with UUID if it's in running state. diff --git a/docs/cuopt/source/cuopt-server/examples/milp/examples/abort_job_example.py b/fern/docs/pages/cuopt-server/examples/milp/examples/abort_job_example.py similarity index 95% rename from docs/cuopt/source/cuopt-server/examples/milp/examples/abort_job_example.py rename to fern/docs/pages/cuopt-server/examples/milp/examples/abort_job_example.py index 144870e896..8ecd998f97 100644 --- a/docs/cuopt/source/cuopt-server/examples/milp/examples/abort_job_example.py +++ b/fern/docs/pages/cuopt-server/examples/milp/examples/abort_job_example.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ Aborting a Running MILP Job Example diff --git a/docs/cuopt/source/cuopt-server/examples/milp/examples/abort_job_example.sh b/fern/docs/pages/cuopt-server/examples/milp/examples/abort_job_example.sh old mode 100755 new mode 100644 similarity index 90% rename from docs/cuopt/source/cuopt-server/examples/milp/examples/abort_job_example.sh rename to fern/docs/pages/cuopt-server/examples/milp/examples/abort_job_example.sh index 8281a9674c..b98ddd38f5 --- a/docs/cuopt/source/cuopt-server/examples/milp/examples/abort_job_example.sh +++ b/fern/docs/pages/cuopt-server/examples/milp/examples/abort_job_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # TEST_SKIP: This is a template file requiring manual configuration diff --git a/docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.py b/fern/docs/pages/cuopt-server/examples/milp/examples/basic_milp_example.py similarity index 96% rename from docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.py rename to fern/docs/pages/cuopt-server/examples/milp/examples/basic_milp_example.py index 113329d888..051a943495 100644 --- a/docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.py +++ b/fern/docs/pages/cuopt-server/examples/milp/examples/basic_milp_example.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ Basic MILP Server Example diff --git a/docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.sh b/fern/docs/pages/cuopt-server/examples/milp/examples/basic_milp_example.sh old mode 100755 new mode 100644 similarity index 93% rename from docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.sh rename to fern/docs/pages/cuopt-server/examples/milp/examples/basic_milp_example.sh index 3e948b6ecf..c4d9820b2b --- a/docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.sh +++ b/fern/docs/pages/cuopt-server/examples/milp/examples/basic_milp_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Basic MILP CLI Example diff --git a/docs/cuopt/source/cuopt-server/examples/milp/examples/incumbent_callback_example.py b/fern/docs/pages/cuopt-server/examples/milp/examples/incumbent_callback_example.py similarity index 100% rename from docs/cuopt/source/cuopt-server/examples/milp/examples/incumbent_callback_example.py rename to fern/docs/pages/cuopt-server/examples/milp/examples/incumbent_callback_example.py diff --git a/fern/docs/pages/cuopt-server/examples/routing-examples.mdx b/fern/docs/pages/cuopt-server/examples/routing-examples.mdx new file mode 100644 index 0000000000..70f85e1b7d --- /dev/null +++ b/fern/docs/pages/cuopt-server/examples/routing-examples.mdx @@ -0,0 +1,87 @@ +--- +title: "Routing Python Examples" +--- + +The following example showcases how to use the `CuOptServiceSelfHostClient` to solve a simple routing problem. + +The OpenAPI specification for the server is available in [open-api spec](../../open-api). The example data is structured as per the OpenAPI specification for the server, please refer [OptimizeRoutingData under "POST /cuopt/request"](../../open-api) under schema section. + +## Generic Example + +[basic\_routing\_example.py](routing/examples/basic_routing_example.py) + +The response would be as follows: + +> +> +> + +## Initial Solution + +Previously run solutions or uploaded solutions can be used as the initial solution for new requests using previously run reqIds as follows: + +[initial\_solution\_example.py](routing/examples/initial_solution_example.py) + +The initial solution in the response is `not accepted`, because the problem is too small, and the optimal solution is found even before cuOpt could use an initial solution. + +The response would be as follows: + +> +> +> + +The `data` argument to `get_optimized_routes` may be a dictionary of the format shown in [Get Routes Open-API spec](../../open-api). It may also be the path of a file containing such a dictionary as JSON or written using the Python *msgpack* module. A JSON file may optionally be compressed with zlib. More details on the responses can be found under the responses schema in ["get /cuopt/request" and "get /cuopt/solution" API spec](../../open-api). + +To enable HTTPS: + + - In the case of the server using public certificates, simply enable https. + + + + - In the case of a self-signed certificate, provide the complete path to the certificate. + + + + You can generate a self-signed certificate easily as follows: + + + + `server.crt` and `server.key` are meant for server, `ca.crt` is meant for client. + +More examples are available in the [Examples Notebooks Repository](https://github.com/NVIDIA/cuopt-examples). + +## Aborting a Running Job in Thin Client + +Please refer to the [aborting-thin-client](#aborting-thin-client) for more details. + +# Routing CLI Examples + +Create a `data.json` file containing this sample data: + +## Routing Example + +Invoke the CLI. + +## Initial Solution in CLI + +To use a previous solution as an initial solution for a new request ID, you are required to save the previous solution, which can be accomplished use option `-k`. Use the previous reqId in the next request as follows: + +## Uploading a Solution + +Users can also upload a solution which might have been saved for later runs. + +## Aborting a Running Job In CLI + +Please refer to the [aborting-cli](#aborting-cli) for more in MILP Example. + + +Please use solver settings while using .mps files. + + +## To enable HTTPS + + - In the case of the server using public certificates, simply enable https. + + + + - In the case of a self-signed certificate, provide the complete path to the certificate. diff --git a/docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.py b/fern/docs/pages/cuopt-server/examples/routing/examples/basic_routing_example.py similarity index 96% rename from docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.py rename to fern/docs/pages/cuopt-server/examples/routing/examples/basic_routing_example.py index 270f9d40db..cc1d6db241 100644 --- a/docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.py +++ b/fern/docs/pages/cuopt-server/examples/routing/examples/basic_routing_example.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ Basic Routing Server Example diff --git a/docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.sh b/fern/docs/pages/cuopt-server/examples/routing/examples/basic_routing_example.sh old mode 100755 new mode 100644 similarity index 89% rename from docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.sh rename to fern/docs/pages/cuopt-server/examples/routing/examples/basic_routing_example.sh index 99d1fff29c..a021ab0aad --- a/docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.sh +++ b/fern/docs/pages/cuopt-server/examples/routing/examples/basic_routing_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Basic Routing CLI Example diff --git a/docs/cuopt/source/cuopt-server/examples/routing/examples/initial_solution_example.py b/fern/docs/pages/cuopt-server/examples/routing/examples/initial_solution_example.py similarity index 97% rename from docs/cuopt/source/cuopt-server/examples/routing/examples/initial_solution_example.py rename to fern/docs/pages/cuopt-server/examples/routing/examples/initial_solution_example.py index c3902e555f..a4423c0404 100644 --- a/docs/cuopt/source/cuopt-server/examples/routing/examples/initial_solution_example.py +++ b/fern/docs/pages/cuopt-server/examples/routing/examples/initial_solution_example.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ Initial Solution Routing Example diff --git a/docs/cuopt/source/cuopt-server/examples/routing/examples/initial_solution_example.sh b/fern/docs/pages/cuopt-server/examples/routing/examples/initial_solution_example.sh old mode 100755 new mode 100644 similarity index 93% rename from docs/cuopt/source/cuopt-server/examples/routing/examples/initial_solution_example.sh rename to fern/docs/pages/cuopt-server/examples/routing/examples/initial_solution_example.sh index 1fefd06b15..00dbaa049b --- a/docs/cuopt/source/cuopt-server/examples/routing/examples/initial_solution_example.sh +++ b/fern/docs/pages/cuopt-server/examples/routing/examples/initial_solution_example.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Initial Solution Routing CLI Example diff --git a/fern/docs/pages/cuopt-server/index.mdx b/fern/docs/pages/cuopt-server/index.mdx new file mode 100644 index 0000000000..b383d91a79 --- /dev/null +++ b/fern/docs/pages/cuopt-server/index.mdx @@ -0,0 +1,11 @@ +--- +title: "Server" +--- + +The **NVIDIA cuOpt self-hosted server** is a **REST** (HTTP/JSON) service for integrations that speak HTTP. Use [Quick Start](quick-start) for deployment, [Index](server-api/index) for the API, and [Index](client-api/index) for clients (including cuopt-sh-client). + +For **gRPC remote execution** (Python, C API, `cuopt_cli`, or custom clients to `cuopt_grpc_server`), see [Index](../cuopt-grpc/index) — it uses a different protocol and is not part of the HTTP REST surface. + +![image](/docs/images/cuOpt-self-hosted.png) + +Please refer to the following sections for REST deployment, API reference, and examples. diff --git a/fern/docs/pages/cuopt-server/nim-operator/configuration.mdx b/fern/docs/pages/cuopt-server/nim-operator/configuration.mdx new file mode 100644 index 0000000000..4e2d256461 --- /dev/null +++ b/fern/docs/pages/cuopt-server/nim-operator/configuration.mdx @@ -0,0 +1,80 @@ +--- +title: "Configuration Guide" +--- + +This guide covers configuration options for the CuOpt NIM Operator deployment. + +## Image Configuration + +### CuOpt Image Versions + +Update the image tag in `cuopt-nimservice.yaml`: + +| CUDA Version | Image Tag | +| ------------ | ------------- | +| CUDA 12.x | `26.6.0-cu12` | +| CUDA 13.x | `26.6.0-cu13` | + +## Resource Configuration + +### GPU Resources + +Configure GPU allocation: + +### Memory Resources + +For workloads requiring specific memory allocation: + +## Environment Variables + +CuOpt supports several environment variables for configuration: + +## Storage Configuration + +The deployment optionally uses persistent storage so that datasets can be passed through the filesystem rather than over http. If data is sent over http (the default), this storage is not needed. + +For custom storage class: + +## Networking Configuration + +### Service Configuration + +Default ClusterIP service: + +For NodePort access: + +For LoadBalancer (cloud environments): .. note:: Currently the cuopt service does not support scaling; there can only be 1 instance of the pod per service. Therefore a LoadBalancer service is unnecessary. + +### Ingress Configuration + +To expose CuOpt externally via ingress: + +With TLS: + +## Scaling Configuration + +Currently the cuOpt service does not support scaling. Only a single instance of the pod per service is supported. + +## Health Probes + +### Liveness Probe + +Determines if the container is running: + +### Readiness Probe + +Determines if the container is ready to accept traffic: + +### Startup Probe + +For slower starting containers: + +## Monitoring Configuration + +Enable Prometheus metrics and ServiceMonitor: + +## Full Configuration Example + +Here's a complete production-ready configuration: + +[cuopt-nimservice-full.yaml](guide/cuopt-nimservice-full.yaml) diff --git a/fern/docs/pages/cuopt-server/nim-operator/deployment.mdx b/fern/docs/pages/cuopt-server/nim-operator/deployment.mdx new file mode 100644 index 0000000000..1fd272a86b --- /dev/null +++ b/fern/docs/pages/cuopt-server/nim-operator/deployment.mdx @@ -0,0 +1,106 @@ +--- +title: "Deployment Guide" +--- + +This guide covers deploying CuOpt using the NIM Operator. + +## Automated Deployment + +The easiest way to deploy CuOpt is using the provided deployment script. + +### Using the Deploy Script + +[deploy.sh](guide/deploy.sh) + +1. Set your NGC API Key: + + + +2. Run the deployment script: + + + +3. With custom options: + + + +### Script Options + +## Manual Deployment + +If you prefer to deploy manually, follow these steps. + +### Step 1: Create Namespace + +[namespace.yaml](guide/namespace.yaml) + +Apply the namespace: + +### Step 2: Create Secrets + +Create the image pull secret: + +Create the NGC API key secret: + +### Step 3: Deploy CuOpt NIMService + +[cuopt-nimservice.yaml](guide/cuopt-nimservice.yaml) + +Apply the NIMService: + +## Verifying the Deployment + +### Check NIMService Status + +Expected output: + +### Check Pods + +### Check Service + +Expected output: + +### Check Logs + +You should see output indicating the server is running: + +## Testing the Deployment + +### Port Forward for Local Testing + +Then access the service at `http://localhost:8000`. + +### Test with cuopt\_sh CLI + +Example output: + +### Health Endpoints + + - Liveness: `GET /v2/health/live` + - Readiness: `GET /v2/health/ready` + +## Cleanup + +### Using the Script + +### Manual Cleanup + +## Troubleshooting + +### Pod Not Starting + +Check pod events: + +Check GPU operator status: + +### Image Pull Errors + +Verify your NGC credentials: + +### Health Check Failures + +Check the readiness and liveness probe logs: + +## Next Steps + +See [configuration](#cuopt-nim-configuration) for advanced configuration options. diff --git a/docs/cuopt/source/cuopt-server/nim-operator/guide/cuopt-nimservice-full.yaml b/fern/docs/pages/cuopt-server/nim-operator/guide/cuopt-nimservice-full.yaml similarity index 100% rename from docs/cuopt/source/cuopt-server/nim-operator/guide/cuopt-nimservice-full.yaml rename to fern/docs/pages/cuopt-server/nim-operator/guide/cuopt-nimservice-full.yaml diff --git a/docs/cuopt/source/cuopt-server/nim-operator/guide/cuopt-nimservice.yaml b/fern/docs/pages/cuopt-server/nim-operator/guide/cuopt-nimservice.yaml similarity index 100% rename from docs/cuopt/source/cuopt-server/nim-operator/guide/cuopt-nimservice.yaml rename to fern/docs/pages/cuopt-server/nim-operator/guide/cuopt-nimservice.yaml diff --git a/docs/cuopt/source/cuopt-server/nim-operator/guide/deploy.sh b/fern/docs/pages/cuopt-server/nim-operator/guide/deploy.sh similarity index 100% rename from docs/cuopt/source/cuopt-server/nim-operator/guide/deploy.sh rename to fern/docs/pages/cuopt-server/nim-operator/guide/deploy.sh diff --git a/docs/cuopt/source/cuopt-server/nim-operator/guide/namespace.yaml b/fern/docs/pages/cuopt-server/nim-operator/guide/namespace.yaml similarity index 100% rename from docs/cuopt/source/cuopt-server/nim-operator/guide/namespace.yaml rename to fern/docs/pages/cuopt-server/nim-operator/guide/namespace.yaml diff --git a/fern/docs/pages/cuopt-server/nim-operator/index.mdx b/fern/docs/pages/cuopt-server/nim-operator/index.mdx new file mode 100644 index 0000000000..ee894accbb --- /dev/null +++ b/fern/docs/pages/cuopt-server/nim-operator/index.mdx @@ -0,0 +1,44 @@ +--- +title: "NIM Operator Deployment" +--- + +This guide walks you through deploying NVIDIA cuOpt as a Kubernetes service using the [NIM Operator](https://docs.nvidia.com/nim-operator/latest/index.html). + +## Overview + +The NIM Operator simplifies the deployment and management of NVIDIA NIM microservices on Kubernetes. This deployment method provides: + + - **Automated lifecycle management** - The operator handles scaling, updates, and health checks + - **Native Kubernetes integration** - Uses standard K8s patterns (CRDs, services, ingress) + - **GPU resource management** - Leverages the GPU Operator for optimal GPU utilization + - **Built-in monitoring** - Prometheus metrics and service monitors + +## Supported GPUs + +The following NVIDIA GPUs are supported: + +| GPU | Architecture | Notes | +| ------------ | ------------ | ------------------------------------- | +| A100 | Ampere | Recommended for production | +| H100 | Hopper | High performance | +| H200 | Hopper | High performance with extended memory | +| B200 | Blackwell | Latest generation | +| RTX 6000 Pro | Ada Lovelace | Workstation GPU | + +## Quick Start + +1. Complete all [prerequisites](#cuopt-nim-prerequisites) + +2. Set your NGC API Key: + + + +3. Run the deployment script: + + + +4. Verify the deployment: + + + +## Documentation Contents diff --git a/fern/docs/pages/cuopt-server/nim-operator/prerequisites.mdx b/fern/docs/pages/cuopt-server/nim-operator/prerequisites.mdx new file mode 100644 index 0000000000..a76ce79a27 --- /dev/null +++ b/fern/docs/pages/cuopt-server/nim-operator/prerequisites.mdx @@ -0,0 +1,98 @@ +--- +title: "Prerequisites" +--- + +This document covers all prerequisites needed before deploying CuOpt with the NIM Operator. + +## Kubernetes Cluster Setup + +You need a Kubernetes cluster with GPU-enabled nodes. Choose one of the following installation methods: + +### Option 1: kubeadm (Manual Setup) + +Standard Kubernetes installation using kubeadm. Follow the [official Kubernetes documentation](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/). + +### Option 2: Cloud Native Stack (Recommended) + +Use NVIDIA Cloud Native Stack Ansible playbooks for automated setup including GPU Operator: + +This method automatically deploys necessary operators including the GPU Operator. + +### Option 3: Minikube (Development/Testing) + +For local development and testing: + +## GPU Operator Installation + +If not using Cloud Native Stack, install the GPU Operator manually. + +### Add NVIDIA Helm Repository + +### Install GPU Operator + +This typically takes 3-5 minutes to install the driver and set up the cloud native stack for GPU usage. + +### Verify GPU Operator + +All pods should be in `Running` state. + +## Storage Provisioner + +CuOpt requires persistent storage. Deploy a storage provisioner if your cluster doesn't have one. + +### Local Path Provisioner (Development/Single Node) + +Wait for the provisioner to be ready: + +### Set Default Storage Class + +### Verify Storage Class + +You should see `local-path` marked as `(default)`. + +### Production Storage Options + +For production deployments, consider: + + - **Cloud providers**: Use native storage classes (AWS EBS, GCP PD, Azure Disk) + - **On-premises**: Longhorn, OpenEBS, Rook-Ceph + +## NIM Operator Installation + +### Create Namespace + +### Install NIM Operator + +### Verify Installation + +You should see the `nimservices.apps.nvidia.com` CRD registered. + +## NGC API Key + +You need an NGC API key to pull NVIDIA container images. + +### Obtain NGC API Key + +1. Go to [NGC](https://ngc.nvidia.com/) +2. Sign in or create an account +3. Navigate to **Setup** → **API Key** +4. Generate a new API key + +### Set Environment Variable + +For persistent configuration, add to your shell profile: + +## Verification Checklist + +Before proceeding with CuOpt deployment, verify: + + - Kubernetes cluster is running (`kubectl cluster-info`) + - GPU nodes are available (`kubectl get nodes -l nvidia.com/gpu.present=true`) + - GPU Operator pods are running (`kubectl get pods -n gpu-operator`) + - Storage class is configured (`kubectl get storageclass`) + - NIM Operator is installed (`kubectl get pods -n nim-operator`) + - NGC API key is set (`echo $NGC_API_KEY`) + +## Next Steps + +Once all prerequisites are met, proceed to [deployment](#cuopt-nim-deployment). diff --git a/fern/docs/pages/cuopt-server/quick-start.mdx b/fern/docs/pages/cuopt-server/quick-start.mdx new file mode 100644 index 0000000000..7d2d657614 --- /dev/null +++ b/fern/docs/pages/cuopt-server/quick-start.mdx @@ -0,0 +1,60 @@ +--- +title: "Quickstart Guide" +--- + +## Installation + +Choose your install method below; the selector is pre-set for the Server. Copy the command and run it in your environment. See [Install](../install) for all interfaces and options. + +
+ +### Container from NVIDIA NGC + +Step 1: Get a subscription for [NVIDIA AI Enterprise (NVAIE)](https://www.nvidia.com/en-us/data-center/products/ai-enterprise/) to get the cuOpt container to host in your cloud. + +Step 2: Once given access, users can find [cuOpt container](https://catalog.ngc.nvidia.com/orgs/nvidia/cuopt/containers/cuopt) in the NGC catalog. + +Step 3: Access NGC registry: + + - Log into NGC using the invite and choose the appropriate NGC org. + - Generate an NGC API key from settings. If you have not generated an API Key, you can generate it by going to the Setup option in your profile and choose Get API Key. Store this or generate a new one next time. More information can be found [here](https://docs.nvidia.com/ngc/latest/ngc-private-registry-user-guide.html#generating-a-personal-api-key). + +Step 4: Pull the container: + + - Go to the container section for cuOpt and copy the pull tag for the latest image. + + - Log into the nvcr.io container registry in your cluster setup, using the NGC API key as shown below. + + > + > + > + + - Pull the container + + > + > + > + +The container includes both the Python API and self-hosted server components. To run the container: + +### NVIDIA Launchable + +NVIDIA cuOpt can be tested with [NVIDIA Launchable](https://brev.nvidia.com/launchable/deploy?launchableID=env-2qIG6yjGKDtdMSjXHcuZX12mDNJ) with [example notebooks](https://github.com/NVIDIA/cuopt-examples). For more details, please refer to the [NVIDIA Launchable documentation](https://docs.nvidia.com/brev/latest/). + +### Smoke Test + +After installation, you can verify that cuOpt Server is working correctly by running a simple test. + + +The following example is for running the server locally. If you are using the container approach, you should comment out the server start and kill commands in the script below since the server is already running in the container. + + +The following example is testing with a simple routing problem constuctured as Json request and sent over HTTP to the server using `curl`.This example is running server with few configuration options such as `--ip` and `--port`. Additional configuration options for server can be found in [Server CLI](server-api/server-cli). + +Install jq and curl for basic HTTP requests and parsing JSON responses + +Run the server and test + +The Open API specification for the server is available in [open-api spec](../open-api). + +Example Response: diff --git a/fern/docs/pages/cuopt-server/server-api/index.mdx b/fern/docs/pages/cuopt-server/server-api/index.mdx new file mode 100644 index 0000000000..7a927cf179 --- /dev/null +++ b/fern/docs/pages/cuopt-server/server-api/index.mdx @@ -0,0 +1,5 @@ +--- +title: "Server API" +--- + +This section contains details on Server options supported and open-api specification for the server. diff --git a/docs/cuopt/source/cuopt-server/server-api/server-cli.rst b/fern/docs/pages/cuopt-server/server-api/server-cli.mdx similarity index 50% rename from docs/cuopt/source/cuopt-server/server-api/server-cli.rst rename to fern/docs/pages/cuopt-server/server-api/server-cli.mdx index c9bef5727d..035def7076 100644 --- a/docs/cuopt/source/cuopt-server/server-api/server-cli.rst +++ b/fern/docs/pages/cuopt-server/server-api/server-cli.mdx @@ -1,10 +1,5 @@ - -======================================== -cuOpt Server CLI -======================================== +--- +title: "cuOpt Server CLI" +--- This section describes the cuOpt Server CLI, which is a command-line interface for the cuOpt Server. Most of these options can also be configured using environment variables. - -.. literalinclude:: server-cli-help.txt - :language: shell - :linenos: diff --git a/fern/docs/pages/faq.mdx b/fern/docs/pages/faq.mdx new file mode 100644 index 0000000000..386cd2ed97 --- /dev/null +++ b/fern/docs/pages/faq.mdx @@ -0,0 +1,449 @@ +--- +title: "FAQ" +--- + +## General FAQ + + + +There are two options: - NVIDIA Docker Hub ([https://hub.docker.com/r/nvidia/cuopt](https://hub.docker.com/r/nvidia/cuopt)) - NVIDIA NGC registry ([https://catalog.ngc.nvidia.com/orgs/nvidia/cuopt/containers/cuopt/tags](https://catalog.ngc.nvidia.com/orgs/nvidia/cuopt/containers/cuopt/tags)) with an NVAIE license or NVIDIA Developer Program membership. + + + + + +Please refer to [NVIDIA NVAIE](https://www.nvidia.com/en-us/data-center/products/ai-enterprise/) for more information. + + + + + +With an NVAIE license or NVIDIA Developer Program membership, you can access the [NGC registry](https://catalog.ngc.nvidia.com/orgs/nvidia/cuopt/containers/cuopt/tags) for cuOpt container images. + + + + + +1. Log into NGC using the invite and choose the appropriate NGC org. + +2. Generate an NGC API key from settings. If you have not generated an API Key, you can generate it by going to the Setup option in your profile and choose [Get API Key](https://docs.nvidia.com/ngc/latest/ngc-private-registry-user-guide.html#generating-a-personal-api-key). Store this or generate a new one next time. + +3. - Go to the container section for cuOpt and copy the pull tag for the latest image. + + - Within the Select a tag dropdown, locate the container image release that you want to run. + - Click the Copy Image Path button to copy the container image path. + +4. Log into the nvcr.io container registry in your cluster setup, using the NGC API key as shown below. + +5. Pull the cuOpt container image. + + + + + +Yes, please refer to [system requirements](system-requirements) for GPU specifications. You can acquire a cloud instance with a supported GPU and launch cuOpt; alternatively, you can launch it in your local machine if it meets the requirements. + + + + + +1. Yes, in cuOpt self-hosted server, a solver process per GPU can be configured to run multiple solvers. Requests are accepted in a round-robin queue. More details are available in [server api](cuopt-server/server-api/server-cli). +2. There is no support for leveraging multiple GPUs to solve a single problem or oversubscribing a single GPU for multiple solvers. + + + + + +1. Check the logs for the container (see cuOpt service monitoring below). + +> Is port 5000 already in use? +> +> If port 5000 is unavailable, the logs will contain an error like this +> +> + +2. Try to locate the process that is using port 5000 and stop it if possible. A tool like `netstat` run as the root user can help identify ports mapped to processes, and `docker ps -a` will show running containers. +3. Alternatively, use port mapping to launch cuOpt on a different port such as 5001 (note the omission of `--network=host` flag): +4. If running locally, you can also use `ps -aux | grep cuopt_server` to find the process and kill it. + +> +> +> + + + + + +1. The time limit supplied governs the run time of the solver only, but there are other overheads such as network delay, ETL, validation or the solver being busy with other requests. +2. The complete round-trip solve time might be more than what was set. + + + + + +The cuOpt wheel links against OpenSSL 3 and intentionally does not bundle `libssl.so.3` / `libcrypto.so.3`; the host must provide them. This keeps libcrypto and any FIPS provider (system or mounted) byte-version-matched at runtime. + +Most modern Linux distributions provide OpenSSL 3 out of the box: Ubuntu 22.04+, Debian 12+, RHEL/Rocky/Alma 9+, and Fedora 36+. For older distributions: + + - **RHEL / Rocky / Alma / CentOS 8**: install the `openssl3` runtime package from EPEL: + + + + - **Ubuntu 20.04**: install OpenSSL 3 from a PPA or backport, or use the cuOpt container image (Ubuntu 22.04 base) instead. + + - **Other distributions**: install your distribution's OpenSSL 3 development/runtime package, or use the cuOpt container. + +After installing, re-run `pip install` (or restart the Python process) and the import should succeed. + + + + + +This error indicates that the cuOpt shared library is not found. Please check the following: + + - The cuOpt is installed + - Use `find / -name libcuopt.so` to search for the library path from root directory. You might need to run this command as root user. + - If the library is found, please add it to the `LD_LIBRARY_PATH` environment variable as shown below: + + - If the library is not found, it means it is not yet installed. Please check the cuOpt installation guide for more details. + + + + + + - We currently don't account for it, since many such overheads are relative and cannot be tracked properly. + + + + + +1. If there are errors pertaining to `rmm` or errors that the service couldn't acquire GPU memory, there is a possibility that GPU memory is being consumed by another process. +2. This can be observed using the command `nvidia-smi`. + + + + + +1. cuOpt microservice health check on the cuOpt host. + +Perform a health-check locally on the host running cuOpt: + +> +> +> +> +> If this command returns 200, cuOpt is running and listening on the specified port. +> +> If this command returns something other than 200, check the following: +> +> - Check that a cuOpt container is running with `docker ps`. +> - Examine the cuOpt container log for errors. +> - Did you include the `--network=host` or a `-p` port-mapping flag to docker when you launched cuOpt? If you used port mapping, did you perform the health check using the correct port? +> - Restart cuOpt and see if that corrects the problem. + +2. cuOpt microservice health-check from a remote host. + +If you are trying to reach cuOpt from a remote host, run the health check from the remote host and specify the IP address of the cuOpt host, for example: + +> +> +> +> +> If this command does not return 200, but a health check locally on the cuOpt host does return 200, the problem is a network configuration or firewall issue. The host is not reachable, or the cuOpt port is not open to incoming traffic. + + + + + +1. This might happen mostly with cuOpt running in a cloud instance. +2. It could be that you are behind a proxy that is generating a certificate chain and you need additional certificate authorities installed on your machine. + +You can examine the certificate chain returned on a connection with the following commands or something similar. If it looks like there are certificates in the chain that are issued by your own organization, contact your local IT admin, and ask them for the proper certificates to install on your machine. + +In this example, we will check the certificate chain being returned from a connection to NVCF at NVIDIA, but you can substitute a different address if you are trying to connect to an instance of cuOpt deployed in the cloud: + + + +## gRPC remote execution (`cuopt_grpc_server`) + + + +Workers write per-job solver logs under `/tmp/cuopt_logs/job_.log`. The `StreamLogs` RPC tails that file. Operational limits and behavior are summarized in [gRPC server behavior](cuopt-grpc/grpc-server-architecture). + + + + + +Jobs that worker was running are marked **FAILED**. The server monitor can detect the crash and spawn a replacement worker; other workers keep running. For more detail, see [gRPC server behavior](cuopt-grpc/grpc-server-architecture) and the contributor reference `cpp/docs/grpc-server-architecture.md` in the repository. + + + + + +Cancellation is honored **before** the solver starts. If the solve has already begun, it **runs to completion**; there is no mid-solve cancellation path. See [gRPC server behavior](cuopt-grpc/grpc-server-architecture). + + + +## Routing FAQ + + + +A waypoint graph is a weighted, directed graph where the weights symbolize cost. Unlike the cost matrix, this graph often represents more than just target locations, including intermediate decision points along a route (locations merely passed through). This method is commonly used for custom environments and indoor spaces, such as warehouses and factories, where the cost between target locations is dynamic or not easily quantifiable. A basic waypoint graph with four nodes is illustrated below: + +![image](/docs/images/faq-01.png) + +Graphs intended for input into cuOpt are shown in **Compressed Sparse Row (CSR)** format for efficiency. The translation from a more conventional (and human-readable) graph format, such as a weighted edge list, to CSR can be accomplished quickly, as depicted below: + +> +> +> + + + + + +1. In some cases, not all vehicles within a fleet are identical. Some might travel faster, while others might incur unaffordable costs when traveling through certain areas. For example, we could have a fleet consisting of planes and trucks. +2. `vehicle_types` can be used along with data such as cost/time matrix for each of the vehicles. Given the example above, planes would have one cost/time matrix, while trucks would have a different cost/time matrix. + + + + + +Use Prize collection, which associates each task with a prize and the solver will maximize the prize collected. This allows cuOpt to prioritize some tasks over others. + + + + + +Some of the metrics need to be equal in size; for example, the number of tasks and their demand. If they don't match, it means the problem is partially defined or there is an issue with the data. + + + + + +For the standard CVRPTW (Capacitated Vehicle Routing Problem with Time Windows) problem with real-world constraints, cuOpt can easily solve for 15K locations with the NVIDIA GPU A100/H100. + + + + + +1. cuOpt routing solver is not deterministic, so the results might vary across multiple runs. Increasing the time limit set for the solver will increase the likelihood of getting identical results across multiple runs. +2. Also, there might be several different solutions with the same cost. + + + + + +1. cuOpt is stateless and cannot handle dynamic constraints directly, but this can be resolved with modeling. +2. Dynamic reoptimization is used when there is a change in the conditions of the operation such as a vehicle getting broken, a driver calling in sick, a road block, traffic, or a high-priority order coming in. +3. The problem is prepped in such a way that the packages that are already en route are assigned to only those vehicles, and new and old deliveries will be added to this problem. Please refer to example notebooks in [cuOpt Resources](resources) to understand more about how to tackle this problem. + + + + + +Currently, cuOpt doesn't accept the initial solution. + + + + + +The units can be whatever the customer wants them to be: minutes, seconds, milliseconds, hours, and so on. It is the user's responsibility to normalize the data across the complete problem, so all time-related constraints use the same unit. For example, if the travel time matrix is given in minutes, we want to make sure time windows and service times are also given in minutes. + + + + + +Currently, we do not have such restrictions, and cuOpt tries to optimize for the fewest number of vehicles as the primary default objective. + + + + + +This is because travel time is not part of the objective, so we could have two solutions that are equivalent when picking the best solution. You can include total travel time (includes wait time) as part of the objective. + + + + + +1. Set high values compared to other actual values, not max of float type. +2. This will ensure this path would not be traversed since it will incur a huge cost. + + + + + +The documentation says `task_locations` should be integers. But in the real world, latitude and longitude coordinates are floating point values. To explain this, read the following section. + +cuOpt expects that a user provides either: + + - A cost matrix and corresponding location indices. + - A waypoint graph and locations corresponding to waypoints as integers. + +So in either case, task locations are actually integer indices into another structure. + +If you have (lat, long) values, then you can generate a cost matrix using a map API. cuOpt does not directly connect to a third-party map engine, but that can be done outside of cuOpt and the resulting cost matrix passed in. + + + + + +Yes, you can define constraints to match vehicles to order type using `vehicle_order_match`. Frozen goods are a great example. + + + + + +This can be observed as a pickup and delivery problem. + + + + + +1. The time limit could be too short. +2. An infeasible solution always provides information about what constraints caused it and which constraint can be relaxed, which might give more hints. + + + + + +Set all prize values = 1 with a very high prize objective (like 10^6), and then set the other objective values for `cost`, `travel_time`, and `route_variance` proportional to each other for cuOpt to always return the best possible solution. + + + + + +1. The routing solver capabilities are based on few factors: + + - The available GPU memory + + - - The size of the problem + + - Number of locations + - Number of vehicles + - Number of tasks + + - - The complexity of the problem + + - Number of demand and capacity constraints + - Number of time windows + - Number of vehicle types + - Number of breaks + + - The time limit + +Depending on these factors, the problems that can be solved can vary, for example: + + - On a H100 SXM with 80GB memory, the maximum number of locations that routing solver can handle is 10,000. + +At the same time, depending on complexity, the solver might be able to handle more or less than 10,000 locations. + + + +## Linear Programming FAQs + + + +LP batch mode is deprecated; see [Batch Mode](#batch-mode) in [Convex Features](convex-features). + +The batch mode allows solving many LPs in parallel to try to fully utilize the GPU when LP problems are too small. Using H100 SXM, the problem should be of at least 1K elements, and giving more than 100 LPs will usually not increase performance. + + + + + +Yes, but we usually see great results on very large and sparse problems. + + + + + +If run on a H100 SXM 80GB (hardware used when using NVIDIA Cloud Functions), you can run the following sizes: + + - 4.5M rows/constraints; 4.5M columns/variables; and 900M non-zeros in the constraint matrix + - 36M rows/constraints; 36M columns/variables; and 720M non-zeros in the constraint matrix + + + + + +There are several ways to tune the solver to get the best possible performance: + + - Hardware: If using self-hosted, you should use a recent server-grade GPU. We recommend H100 SXM (not the PCIE version). + - Tolerance: The set tolerance usually has a massive impact on performance. Try the lowest possible value using `set_optimality_tolerance` until you have reached your lowest possible acceptable accuracy. + - PDLP Solver mode: PDLP solver mode will change the way PDLP internally optimizes the problem. The mode choice can drastically impact how fast a specific problem will be solved. You should test the different modes to see which one fits your problem best. + - Batch mode: Deprecated for LP; prefer sequential `cuopt.linear_programming.Solve` calls (see [Batch Mode](#batch-mode)). While available, batch mode can solve multiple LPs in parallel. + - Presolve: Presolve can reduce problem size and improve solve time. + + + + + +We cannot predict up-front which solver mode will work best for a particular problem. The only way to know is to test. Once you know a solver mode is good on a class of problems, it should also be good on other similar problems. + + + + + +The choice entirely depends on the level of accuracy you need for your problem. A looser tolerance will always result in a faster result. For PDLP, 1e-2 relative tolerance is low accuracy, 1e-4 is regular, 1e-6 is high, and 1e-8 is very high. + + + + + +1. There is no inherit limit imposed on the number of variables, number of constraints, or number of non-zeros you can have in a MILP or LP, except the restrictions due to the number of bits in an integer and the amount of memory in the CPU and GPU. + +Depending on these factors, the problems that can be solved can vary, for example: + + - - On a H100 SXM with 80GB memory, here are few examples of the problems that can be solved: + + - 10M rows/constraints, 10M columns/variables, and 2B non-zeros in the constraint matrix. + - 74.5M rows/constraints, 74.5M columns/variables, and 1.49B non-zeros in the constraint matrix. + + + + + +cuOpt supports presolve reductions using PSLP or Papilo for linear programming (LP) problems, and Papilo for mixed-integer programming (MIP) problems. For MIP problems, Papilo presolve is always enabled by default. For LP problems, PSLP presolve is always enabled by default. Presolve is controlled by the `CUOPT_PRESOLVE` setting. + + + + + +To use warm start functionality with PDLP, you must explicitly disable presolve by setting `CUOPT_PRESOLVE=0` in solver\_config. This is required because presolve transforms the problem, and the warm start solution from the original problem cannot be applied to the presolved problem. + + + +## Mixed Integer Linear Programming FAQs + + + +You can run the cuOpt MIP solver through the Python, C, CLI, or server APIs by providing a model with integer or binary variables. The cuOpt MIP solver is in **beta** and under active development. The solver currently excels at finding high-quality feasible solutions quickly with GPU-accelerated primal heuristics. Proving feasible solutions optimal remains under active development. + + + + + +1. There is no inherit limit imposed on the number of variables, number of constraints, or number of non-zeros you can have in a MILP or LP, except the restrictions due to the number of bits in integer and the amount of memory in the CPU and GPU. + +Depending on these factors, the problems that can be solved can vary, for example: + + - - On a H100 SXM with 80GB memory, this is the biggest dataset that was tested: + + - 27 million non-zeros coefficients on a problem from MIPLIB2017. + + + +## Container FAQs + + + +You can share only selected GPUs in a container by using the `--gpus` flag. For example, to share only the first GPU, you can use the following command: + + + + + +You can run cuOpt container with options set as environment variables by using the `--env` flag. For example, to set the time limit to 1000 seconds, you can use the following command: + + diff --git a/fern/docs/pages/index.mdx b/fern/docs/pages/index.mdx new file mode 100644 index 0000000000..97347d445f --- /dev/null +++ b/fern/docs/pages/index.mdx @@ -0,0 +1,3 @@ +--- +title: "NVIDIA cuOpt" +--- diff --git a/fern/docs/pages/install.mdx b/fern/docs/pages/install.mdx new file mode 100644 index 0000000000..799208fc11 --- /dev/null +++ b/fern/docs/pages/install.mdx @@ -0,0 +1,21 @@ +--- +title: "Installation" +--- + +Choose your interface, install method, and options below to get the exact install command. Ensure your system meets the [System Requirements](system-requirements) before installing. + +## Install Selector + +
+ +## Quick Start Guides + +If the selector does not load or you prefer step-by-step guides, use the quick-start pages for each interface: + + - **Python (cuopt)** — [Quick Start](cuopt-python/quick-start) + - **C (libcuopt)** — [Quick Start](cuopt-c/quick-start) (includes `cuopt_cli`) + - **gRPC remote execution** — [Quick Start](cuopt-grpc/quick-start) (install, remote execution, Docker, minimal example) and [Advanced](cuopt-grpc/advanced) (TLS and tuning; not the HTTP server) + - **Server (cuopt-server)** — [Quick Start](cuopt-server/quick-start) + - **CLI (cuopt\_cli)** — Install via the C API; see [Quick Start](cuopt-cli/quick-start) + +See [System Requirements](system-requirements) for GPU, CUDA, driver, and OS requirements. diff --git a/fern/docs/pages/introduction.mdx b/fern/docs/pages/introduction.mdx new file mode 100644 index 0000000000..48b793252c --- /dev/null +++ b/fern/docs/pages/introduction.mdx @@ -0,0 +1,161 @@ +--- +title: "Introduction" +--- + +**NVIDIA® cuOpt™** is a GPU-accelerated optimization library that solves [Linear Programming (LP)](https://en.wikipedia.org/wiki/Linear_programming), [Quadratic Programming (QP)](https://en.wikipedia.org/wiki/Quadratic_programming), and [Vehicle Routing Problems (VRP)](https://en.wikipedia.org/wiki/Vehicle_routing_problem), with support for [Quadratically Constrained Quadratic Programming (QCQP)](https://en.wikipedia.org/wiki/Quadratically_constrained_quadratic_program) (beta), [Second-Order Cone Programming (SOCP)](https://en.wikipedia.org/wiki/Second-order_cone_programming) (beta), and [Mixed Integer Linear Programming (MILP)](https://en.wikipedia.org/wiki/Linear_programming#Integer_unknowns) (beta). It enables solutions for large-scale problems with millions of variables and constraints, offering seamless deployment across hybrid and multi-cloud environments. + +Using accelerated computing, NVIDIA® cuOpt optimizes operations research and logistics by enabling better, faster decisions. + +As part of [NVIDIA AI Enterprise](https://www.nvidia.com/en-us/data-center/products/ai-enterprise/), NVIDIA cuOpt offers a secure, efficient way to rapidly generate world-class route optimization solutions. Using a single optimized container, you can deploy the AI microservice in under 5 minutes on accelerated NVIDIA GPU systems in the cloud, data center, workstations, or PCs. A license for NVIDIA AI Enterprise or membership in the NVIDIA Developer Program is required. For more information about NVAIE licensing, accessing NGC registry, and pulling container images, please refer to the [FAQ section](faq). + + +NVAIE support is extended to only cuOpt Routing service API. LP and MILP are not supported as part of it, they are just add-ons. + + + +Check out this [FAQ](https://forums.developer.nvidia.com/t/nvidia-nim-faq/300317) for more information about the NVIDIA Developer Program. + + +The core engine is built on C++ and all the APIs are built on top of it as wrappers. For example, cuOpt Python API uses Cython to wrap the C++ core engine and provide a Python interface. Similarly, other interfaces wrap different layers to communicate with the core engine. + +## Routing (TSP, VRP, and PDP) + +The **Vehicle Routing Problem (VRP)** and **Pickup and Delivery Problems (PDP)** are derived from the **Traveling Salesperson Problem (TSP)**, which is one of the most studied problems in operations research and, more generally, in computer science. + +TSP asks the following question: + +> - Given a list of destinations and a matrix of distances between each pair of destinations, what is the shortest possible route that visits each destination exactly one time and returns to the original location? + +For example, the TSP has several applications in planning and logistics, where a good solution can save significant travel time and fuel costs in the transportation and delivery of goods. VRP and PDP are essentially extensions of TSP with additional complexity. + +The VRP generalizes the TSP to solve for the optimal set of routes for a fleet of vehicles in order to deliver to a given set of customers. The PDP adds the possibility of two different types of services, namely pickup or delivery, whereas in VRP all customers require the same service be performed at a customer location. + +### How cuOpt Solves the Routing Problem + +cuOpt first generates an initial population of solutions, then iteratively improves the population until the time limit is reached, and picks the best solution from the population. + +### The Necessity for Heuristics + +Given the time and computational resources required for brute-force enumeration, obtaining the exact optimal solution is not realistic. However, there are well-studied heuristics that yield near-optimal solutions for very large problems within a reasonable time, and NVIDIA cuOpt focuses on using these heuristics. + +## Linear Programming (LP) + +**Linear Programming** is a technique for optimizing a linear objective function over a feasible region defined by a set of linear inequality and equality constraints. For example, consider the following system constraints + +> 2x + 4y \>= 230 +> +> 3x + 2y <= 190 +> +> x \>= 0 +> +> y \>= 0, + +and suppose we want to maximize the objective function + +> f(x,y) = 5x + 3y. + +This is a linear program. + +### How cuOpt Solves the Linear Programming Problem + +cuOpt includes three LP solving methods: + + - **PDLP**: Based on [PDLP](https://arxiv.org/abs/2106.04756), a First-Order Method (FOM) for solving large-scale LPs. This solver implements primal-dual hybrid gradient enhanced by heuristics. Sparse matrix-vector products are perfomed efficiently on NVIDIA GPUs. + - **Barrier (Interior-Point)**: A primal-dual interior-point method that uses GPU-accelerated sparse Cholesky and LDLT solves via cuDSS, and sparse matrix operations via cuSparse. + - **Dual Simplex**: A CPU-based dual simplex solver for small to medium-sized problems. + +All three algorithms can be run concurrently on both GPU and CPU, with the fastest solution returned automatically. + +## Mixed Integer Linear Programming (MILP) (Beta) + +A **Mixed Integer Program (MIP)** is an optimization problem where some variables are restricted to take on only integer values, while other variables can vary continuously. A **Mixed Integer Linear Program (MILP)** is a MIP with a linear objective and linear constraints. + + +The cuOpt MIP solver is in **beta** and under active development. The solver currently excels at finding high-quality feasible solutions quickly with GPU-accelerated primal heuristics. Proving feasible solutions optimal remains under active development. + + +For example, consider the following system of constraints: + +> 2x + 4y \>= 230 +> +> 3x + 2y <= 190 +> +> x \>= 0 and x is integer +> +> y \>= 0 and y is continuous, + +and suppose we wish to maximize the objective function + +> f(x,y) = 5x + 3y. + +This is a mixed integer linear program. + +Although MILPs seems similar to a LPs, they require much more computation to solve. + +### How cuOpt Solves the Mixed-Integer Linear Programming Problem + +cuOpt combines GPU-accelerated primal heuristics for improving the primal bound with traditional CPU algorithms, including branch and bound, to improve the dual bound. Primal heuristics such as local search, feasibility pump, and feasibility jump run on the GPU. Integer feasible solutions are shared between these components. + +# Supported APIs + +cuOpt supports the following APIs: + + - - C API support + + - [Linear Programming (LP) / Quadratic Programming (QP) - C](cuopt-c/quick-start) + - [Mixed Integer Linear Programming (MILP) - C](cuopt-c/quick-start) + + - - C++ API support + + - cuOpt is written in C++ and includes a native C++ API. However, we do not provide documentation for the C++ API at this time. We anticipate that the C++ API will change significantly in the future. Use it at your own risk. + + - - Python support + + - [Routing (TSP, VRP, and PDP) - Python](cuopt-python/quick-start) + - [Linear Programming (LP) / Quadratic Programming (QP) and Mixed Integer Linear Programming (MILP) - Python](cuopt-python/quick-start) + + - - gRPC remote execution + + - [Linear Programming (LP) / Quadratic Programming (QP) and Mixed Integer Linear Programming (MILP) - gRPC remote](cuopt-grpc/quick-start) + + - - Server support + + - [Linear Programming (LP) - Server](cuopt-server/quick-start) + - [Mixed Integer Linear Programming (MILP) - Server](cuopt-server/quick-start) + - [Routing (TSP, VRP, and PDP) - Server](cuopt-server/quick-start) + + - - Third-party modeling languages + + - [AMPL](https://www.ampl.com/) + - [GAMS](https://www.gams.com/) + - [PuLP](https://pypi.org/project/PuLP/) + - [JuMP](https://github.com/jump-dev/cuOpt.jl) + - [Pyomo](https://www.pyomo.org/) + - [CVXPY](https://www.cvxpy.org/) + +# Installation Options + +NVIDIA cuOpt is available in several formats to suit different deployment needs: + +## Source Code + +For users who want to customize cuOpt or contribute to its development, the source code is available on [GitHub](https://github.com/NVIDIA/cuopt). Building from source allows maximum flexibility but requires setting up the build environment. + +## Pip Wheels + +For Python users with existing pip-based workflows, cuOpt can be installed directly via pip from the NVIDIA Python Package Index. This is the simplest installation method for most users. + +## Conda Packages + +Available from the NVIDIA channel, conda packages provide a convenient way to manage cuOpt and its dependencies in conda environments. This is ideal for users who prefer conda-based workflow management. + +## Containers + +NVIDIA provides ready-to-use containers with cuOpt pre-installed, available from: + + - Docker Hub (`nvidia/cuopt`) + - NVIDIA NGC (for NVIDIA AI Enterprise subscribers) + +Containers offer a consistent, isolated environment and are particularly useful for cloud deployments or microservices architectures. + +For detailed installation instructions for each option, please refer to the respective quickstart guides in the documentation. diff --git a/fern/docs/pages/license.mdx b/fern/docs/pages/license.mdx new file mode 100644 index 0000000000..0a43a76fac --- /dev/null +++ b/fern/docs/pages/license.mdx @@ -0,0 +1,3 @@ +--- +title: "cuOpt License" +--- diff --git a/fern/docs/pages/milp-features.mdx b/fern/docs/pages/milp-features.mdx new file mode 100644 index 0000000000..2ad095a152 --- /dev/null +++ b/fern/docs/pages/milp-features.mdx @@ -0,0 +1,77 @@ +--- +title: "MILP Features" +--- + +## Availability + +The cuOpt MIP solver is in **beta** and under active development. The solver currently excels at finding high-quality feasible solutions quickly with GPU-accelerated primal heuristics. Proving feasible solutions optimal remains under active development. + +The MILP solver can be accessed in the following ways: + + - **Third-Party Modeling Languages**: cuOpt's LP and MILP solver can be called directly from the following third-party modeling languages. This allows you to leverage GPU acceleration while maintaining your existing optimization workflow in these modeling languages. + - Currently supported solvers: + + - AMPL + - GAMS + - PuLP + - JuMP + - Pyomo + - **C API**: A native C API that provides direct low-level access to cuOpt's MILP solver, enabling integration into any application or system that can interface with C. + - **Python SDK**: A Python package that provides direct access to cuOpt's MILP capabilities through a simple, intuitive API. This allows for seamless integration into Python applications and workflows. For more information, see [Quick Start](cuopt-python/quick-start). + - **As a Self-Hosted Service**: cuOpt's MILP 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. + +## Variable Bounds + +Lower and upper bounds can be applied to each variable. If no variable bounds are specified, the default bounds are `[-inf,+inf]`. + +## Constraints + +The constraint matrix is specified in [Compressed Sparse Row (CSR) format](https://docs.nvidia.com/cuda/cusparse/#compressed-sparse-row-csr). + +There are two ways to specify constraints in cuOpt MILP: + +1. Using row\_type and right-hand side: + + Constraints can be specified in the form: + + A\*x \{<=, =, \>=\} b + + where A is the constraint matrix in CSR format, x is the variable vector, and b is the right-hand side vector. The relationship \{<=, =, \>=\} is specified through the `row_type` parameter. + +2. Using constraint bounds: + + Alternatively, constraints can be specified as two-sided inequalities: + + lb <= A\*x <= ub + + where lb and ub are vectors of lower and upper bounds respectively. This form allows specifying both bounds of a single constraint. + +Both forms are mathematically equivalent. The choice between them is a matter of convenience depending on your problem formulation. + +## Incumbent Solution Callback in the Service + +When using the service, users can provide a callback to receive new integer feasible solutions that improve the objective (called incumbents) while the solver is running. An [Incumbent Example](#incumbent-and-logging-callback) is shared on the self-hosted page. + +## Logging + +The CUOPT\_LOG\_FILE parameter can be set to write detailed solver logs for MILP problems. This parameter is available in all APIs that allow setting solver parameters except for the cuOpt service. For the service, see the logging callback below. + +## Logging Callback in the Service + +In the cuOpt service API, the `log_file` value in `solver_configs` is ignored. + +If however you set the `solver_logs` flag on the `/cuopt/request` REST API call, users can fetch the log file content from the webserver at `/cuopt/logs/{id}`. Using the logging callback feature through the cuOpt client is shown in [Logging Callback Example](#incumbent-and-logging-callback) on the self-hosted page. + +## Time Limit + +The user may specify a time limit to the solver. By default the solver runs until a solution is found or the problem is determined to be infeasible or unbounded. + + +Note that `time_limit` applies only to solve time inside the LP solver. This does not include time for network transfer, validation of input, and other operations that occur outside the solver. The overhead associated with these operations are usually small compared to the solve time. + + +## Node Limit + +The user may specify a maximum number of branch-and-bound nodes via `node_limit`. The solver stops once this limit is reached and returns the current best feasible solution, if any. If both `time_limit` and `node_limit` are set, the solver stops at whichever limit is hit first. By default there is no node limit. diff --git a/fern/docs/pages/mip-settings.mdx b/fern/docs/pages/mip-settings.mdx new file mode 100644 index 0000000000..4bd6097e8c --- /dev/null +++ b/fern/docs/pages/mip-settings.mdx @@ -0,0 +1,358 @@ +--- +title: "MIP Settings" +--- + +This page describes the parameter settings available for cuOpt's MIP solver. These parameters are set as [parameter constants](#parameter-constants) in case of C API and in case of Server Thin client as raw strings. Refer to examples in [C](cuopt-c/mip/index) and [Server Thin client](cuopt-server/index) for more details. + + +When setting parameters in thin client solver settings, remove `CUOPT_` from the parameter name and convert to lowercase. For example, `CUOPT_TIME_LIMIT` would be set as `time_limit`. + + +## Common Parameters + +We begin by describing parameters common to both the MILP and LP solvers. + +### Time Limit + +`CUOPT_TIME_LIMIT` controls the time limit in seconds after which the solver will stop and return the current solution. For performance reasons, cuOpt does not constantly checks for time limit. Thus, the solver may run slightly over the limit. If set along with the iteration limit, cuOpt will stop when the first limit (iteration or time) is hit. + + +By default there is no time limit. So cuOpt will run until it finds an optimal solution, or proves the problem is infeasible or unbounded. + + +### Log to Console + +`CUOPT_LOG_TO_CONSOLE` controls whether cuOpt should log information to the console during a solve. If true, a logging info is written to the console; if false no logging info is written to the console (logs may still be written to a file.) + + +The default value is true. + + +### Log File + +`CUOPT_LOG_FILE` controls the name of a log file where cuOpt should write information about the solve. + + +The default value is `""` and no log file is written. This setting is ignored by the cuOpt service, use the log callback feature instead. + + +### Solution File + +`CUOPT_SOLUTION_FILE` controls the name of a file where cuOpt should write the solution. + + +The default value is `""` and no solution file is written. This setting is ignored by the cuOpt service. + + +### User Problem File + +`CUOPT_USER_PROBLEM_FILE` controls the name of a file where cuOpt should write the user problem. + + +The default value is `""` and no user problem file is written. This setting is ignored by the cuOpt service. + + +### Num CPU Threads + +`CUOPT_NUM_CPU_THREADS` controls the number of CPU threads used in the MIP solver. Set this to a small value to limit the amount of CPU resources cuOpt uses. Set this to a large value to improve solve times for CPU parallel parts of the solvers. + + +By default the number of CPU threads is automatically determined based on the number of CPU cores. + + +### Presolve + +`CUOPT_PRESOLVE` controls whether to apply presolve reductions. Set this to 0 to disable presolve. + + +By default, presolve is enabled. + + +### Probing + +`CUOPT_MIP_PROBING` toggles the probing-cache step of cuOpt's internal MIP presolve. The probing pass evaluates variable fixings to discover implications used later by branch-and-bound and the rounding heuristics. It is enabled by default (`true`); set it to `false` to skip probing while leaving the rest of presolve in place. Setting `CUOPT_PRESOLVE=0` already turns off the entire presolve pipeline, so `CUOPT_MIP_PROBING` only matters when presolve is otherwise enabled. Probing is also skipped in deterministic mode and on LP-only solves. + +### Dual Postsolve + +`CUOPT_DUAL_POSTSOLVE` controls whether dual postsolve is enabled when using Papilo presolver for LP problems. Disabling dual postsolve can improve solve time at the expense of not having access to the dual solution. Enabled by default for LP when Papilo presolve is selected. This is not relevant for MIP problems. + +## Mixed Integer Linear Programming + +The cuOpt MIP solver is in **beta** and under active development. The solver currently excels at finding high-quality feasible solutions quickly with GPU-accelerated primal heuristics. Proving feasible solutions optimal remains under active development. + +We now describe parameter settings for the MILP solver. + +### Heuristics Only + +`CUOPT_MIP_HEURISTICS_ONLY` controls if only the GPU heuristics should be run for the MIP problem. When set to true, only the primal bound is improved via the GPU. When set to false, both the GPU and CPU are used and the dual bound is improved on the CPU. + + +The default value is false. + + +### Scaling + +`CUOPT_MIP_SCALING` controls if scaling should be applied to the MIP problem. + + - `0`: Scaling is off. + - `1`: Scaling is on. + - `2`: Scaling is not applied to the objective (default). + +### Absolute Tolerance + +`CUOPT_MIP_ABSOLUTE_TOLERANCE` controls the MIP absolute tolerance. + + +The default value is `1e-6`. + + +### Relative Tolerance + +`CUOPT_MIP_RELATIVE_TOLERANCE` controls the MIP relative tolerance. + + +The default value is `1e-12`. + + +### Integrality Tolerance + +`CUOPT_MIP_INTEGRALITY_TOLERANCE` controls the MIP integrality tolerance. A variable is considered to be integral, if it is within the integrality tolerance of an integer. + + +The default value is `1e-5`. + + +### Absolute MIP Gap + +`CUOPT_MIP_ABSOLUTE_GAP` controls the absolute tolerance used to terminate the MIP solve. The solve terminates when: + + Best Objective - Dual Bound <= absolute tolerance + +when minimizing or + +> Dual Bound - Best Objective <= absolute tolerance + +when maximizing. + + +The default value is `1e-10`. + + +### Relative MIP Gap + +`CUOPT_MIP_RELATIVE_GAP` controls the relative tolerance used to terminate the MIP solve. The solve terminates when: + + abs(Best Objective - Dual Bound) / abs(Best Objective) <= relative tolerance + +If the Best Objective and the Dual Bound are both zero the gap is zero. If the best objective value is zero, the gap is infinity. + + +The default value is `1e-4`. + + +### Node Limit + +`CUOPT_NODE_LIMIT` controls the maximum number of branch-and-bound nodes the MILP solver will explore before stopping and returning the current best feasible solution (if any). If set along with the time limit, cuOpt stops at whichever limit is hit first. + + +By default there is no node limit. + + +### Cut Passes + +`CUOPT_MIP_CUT_PASSES` controls the maximum number of cut passes to run. Set this value to 0 to disable cuts. Set this value to larger numbers to perform more cut passes. + + +The default value is `10`. + + +### Mixed Integer Rounding Cuts + +`CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS` controls whether to use mixed integer rounding cuts. The default value of `-1` (automatic) means that the solver will decide whether to use mixed integer rounding cuts based on the problem characteristics. Set this value to 1 to enable mixed integer rounding cuts. Set this value to 0 to disable mixed integer rounding cuts. + + +The default value is `-1` (automatic). + + +### Mixed Integer Gomory Cuts + +`CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS` controls whether to use mixed integer Gomory cuts. The default value of `-1` (automatic) means that the solver will decide whether to use mixed integer Gomory cuts based on the problem characteristics. Set this value to 1 to enable mixed integer Gomory cuts. Set this value to 0 to disable mixed integer Gomory cuts. + + +The default value is `-1` (automatic). + + +### Strong Chvatal-Gomory Cuts + +`CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS` controls whether to use strong Chvatal-Gomory cuts. The default value of `-1` (automatic) means that the solver will decide whether to use strong Chvatal-Gomory cuts based on the problem characteristics. Set this value to 1 to enable strong Chvatal Gomory cuts. Set this value to 0 to disable strong Chvatal Gomory cuts. + + +The default value is `-1` (automatic). + + +### Knapsack Cuts + +`CUOPT_MIP_KNAPSACK_CUTS` controls whether to use knapsack cuts. The default value of `-1` (automatic) means that the solver will decide whether to use knapsack cuts based on the problem characteristics. Set this value to 1 to enable knapsack cuts. Set this value to 0 to disable knapsack cuts. + + +The default value is `-1` (automatic). + + +### Cut Change Threshold + +`CUOPT_MIP_CUT_CHANGE_THRESHOLD` controls the threshold for the improvement in the dual bound per cut pass. Larger values require the dual bound to improve significantly in each cut pass. Set this value to -1 to allow the cut passes to continue even if the dual bound does not improve. + + +The default value is `-1` (no threshold). + + +### Cut Min Orthogonality + +`CUOPT_MIP_CUT_MIN_ORTHOGONALITY` controls the minimum orthogonality required for a cut to be added to the LP relaxation. Set this value close to 1, to require all cuts be close to orthogonal to each other. Set this value close to zero to allow more cuts to be added to the LP relaxation. + + +The default value is `0.5`. + + +### Reduced Cost Strengthening + +`CUOPT_MIP_REDUCED_COST_STRENGTHENING` controls whether to use reduced-cost strengthening. When enabled, the solver will use integer feasible solutions to strengthen the bounds of integer variables. The default value of `-1` (automatic) means that the solver will decide whether to use reduced-cost strengthening based on the problem characteristics. Set this value to 0 to disable reduced-cost strengthening. Set this value to 1 to perform reduced-cost strengthening during the root cut passes. Set this value to 2 to perform reduced-cost strengthening during the root cut passes and after strong branching. + + +The default value is `-1` (automatic). + + +### Reliability Branching + +`CUOPT_MIP_RELIABILITY_BRANCHING` controls the reliability branching mode. The default value of `-1` (automatic) means that the solver will decide whether to use reliability branching, and the reliability branching factor, based on the problem characteristics. Set this value to 0 to disable reliability branching. Set this value to k \> 0, to enable reliability branching. A variable will be considered reliable if it has been branched on k times. + + +The default value is `-1` (automatic). + + +### Batch PDLP Strong Branching + +`CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING` controls whether to use batched PDLP over Dual Simplex during strong branching at the root. When enabled, the solver evaluates multiple branching candidates simultaneously in a single batched PDLP solve rather than solving them in parallel using Dual Simplex. This can significantly reduce the time spent in strong branching if Dual Simplex is struggling. Set this value to 0 to disable batched PDLP strong branching. Set this value to 1 to enable batched PDLP strong branching. + + +The default value is `0` (disabled). This setting is ignored if the problem is not a MIP problem. + + +### Batch PDLP Reliability Branching + +`CUOPT_MIP_BATCH_PDLP_RELIABILITY_BRANCHING` controls whether to use batched PDLP for reliability branching evaluations. When enabled, candidate variables for reliability branching are evaluated simultaneously using a single batched PDLP solve. Set this value to 0 to disable. Set this value to 1 to enable. + + +The default value is `0` (disabled). + + +### Strong Branching Simplex Iteration Limit + +`CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITERATION_LIMIT` controls the maximum number of simplex iterations allowed per candidate during strong branching. Reducing this value speeds up strong branching at the cost of less accurate candidate evaluations. + + +The default value is `-1` (choose the iteration limit automatically). + + +### MIP Determinism Mode + +`CUOPT_MIP_DETERMINISM_MODE` controls whether the MIP solver runs in opportunistic or deterministic mode. + + - `0` (`CUOPT_MODE_OPPORTUNISTIC`): Opportunistic mode — results may vary between runs due to parallelism (default). + - `1` (`CUOPT_MODE_DETERMINISTIC`): Deterministic mode — improves reproducibility across runs with the same number of threads. + + +The default value is `0` (opportunistic). + + + +Deterministic mode is experimental. It improves reproducibility in many cases but does not yet guarantee fully deterministic results in all scenarios. + + +### MIP Symmetry + +`CUOPT_MIP_SYMMETRY` controls symmetry detection and handling in the MIP solver. + + - `-1`: Automatic (default) — cuOpt decides based on problem characteristics. + - `0`: Disable symmetry handling. + - `1`: Enable symmetry handling (orbital fixing only). + - `2`: Enable symmetry handling (orbital fixing + lexical reduction). + + +The default value is `-1` (automatic). + + +### Flow Cover Cuts + +`CUOPT_MIP_FLOW_COVER_CUTS` controls whether to use flow cover cuts. Set this value to `-1` (automatic) to let the solver decide. Set this value to `0` to disable flow cover cuts. Set this value to `1` to enable flow cover cuts. + + +The default value is `-1` (automatic). + + +### Implied Bound Cuts + +`CUOPT_MIP_IMPLIED_BOUND_CUTS` controls whether to use implied bound cuts. Set this value to `-1` (automatic) to let the solver decide. Set this value to `0` to disable implied bound cuts. Set this value to `1` to enable implied bound cuts. + + +The default value is `-1` (automatic). + + +### Clique Cuts + +`CUOPT_MIP_CLIQUE_CUTS` controls whether to use clique cuts. Set this value to `-1` (automatic) to let the solver decide. Set this value to `0` to disable clique cuts. Set this value to `1` to enable clique cuts. + + +The default value is `-1` (automatic). + + +### Zero-Half Cuts + +`CUOPT_MIP_ZERO_HALF_CUTS` controls whether to use zero-half cuts. Set this value to `-1` (automatic) to let the solver decide. Set this value to `0` to disable zero-half cuts. Set this value to `1` to enable zero-half cuts. + + +The default value is `-1` (automatic). + + +### Objective Step + +`CUOPT_MIP_OBJECTIVE_STEP` controls whether cuOpt automatically detects and exploits discrete step structure in the objective to tighten the dual bound. + + - `0`: Disable objective step detection. + - `1`: Enable objective step detection (default). + + +The default value is `1` (enabled). + + +### Semi-Continuous Big-M + +`CUOPT_MIP_SEMICONTINUOUS_BIG_M` controls the Big-M coefficient used when linearizing semi-continuous variable constraints. A semi-continuous variable is either zero or takes a value in the range `[lower_bound, upper_bound]`. Set this to a positive value that is at least as large as the upper bound of any semi-continuous variable in the problem. + + +By default cuOpt derives the Big-M from the variable's upper bound. + + +### Work Limit + +`CUOPT_WORK_LIMIT` controls the work limit after which the solver will stop and return the current solution. Work units are a machine-independent measure of solver effort. If set along with the time limit or iteration limit, cuOpt will stop when the first limit is hit. + + +By default there is no work limit. + + +### Random Seed + +`CUOPT_RANDOM_SEED` controls the random seed used by the solver. Setting a fixed seed enables reproducible results when running in deterministic mode. + + +By default the random seed is set automatically. + + +### Presolve File + +`CUOPT_PRESOLVE_FILE` controls the name of a file where cuOpt should write presolve information. + + +The default value is `""` and no presolve file is written. + diff --git a/fern/docs/pages/open-api.mdx b/fern/docs/pages/open-api.mdx new file mode 100644 index 0000000000..b6fec806d2 --- /dev/null +++ b/fern/docs/pages/open-api.mdx @@ -0,0 +1,5 @@ +--- +title: "cuOpt Open-API Reference - Swagger" +--- + +The interactive REST API explorer is available in the **REST API Reference** section of the left navigation. It covers all cuOpt server endpoints for LP, MIP, and routing optimization problems. diff --git a/fern/docs/pages/release-notes.mdx b/fern/docs/pages/release-notes.mdx new file mode 100644 index 0000000000..3ba6a8518e --- /dev/null +++ b/fern/docs/pages/release-notes.mdx @@ -0,0 +1,399 @@ +--- +title: "Release Notes" +--- + +## Release Notes 26.06 + +### New Features (26.06) +- Add support for quadratic constraints (convex quadratic, second-order cone, and rotated second-order cone) in the barrier solver +- Add support for semi-continuous variables in the MIP solver +- Add support for reading LP (.lp) files; LP files are accepted wherever MPS files were previously, including the compressed variants (`.lp.gz`, `.lp.bz2`) +- Branch and bound workers now maintain their own heaps and steal nodes from other workers; on average, this increases the number of nodes explored by 3x given a fixed time limit +- Add support for detecting and exploiting symmetry in MIP +- Add support for flow cover cuts in MIP +- Add support for detecting and exploiting discrete objective steps in MIP +- Add support for handling free variables directly in barrier's augmented system when a problem has a quadratic objective or constraints +- New right-looking Markowitz LU factorization for dual simplex and crossover +- Add setting for specifying a limit on the number of nodes explored in branch and bound +- Add setting for disabling probing in cuOpt MIP presolve + +### Breaking Changes (26.06) +- Routing API: drop `const` qualifier in `raft::handle_t` on the `data_model_view_t` +- Move `libmps_parser` into `libcuopt` and the `mps_parser` Python module into `cuopt`; the standalone `libmps_parser` shared library and Python module are no longer shipped. Imports should move from `libmps_parser` to `cuopt`. Refer to https://github.com/NVIDIA/cuopt/pull/1193 + +### Improvements (26.06) +- Replace SpMV calls with SpMVOp calls in PDLP; 9% faster +- Improve performance of QP solver on portfolio optimization problems +- Improve performance of dual simplex (remove unnecessary BTran, swap coefficients for faster reduced cost calculation, trigger basis refactorization based on work limits); 16% faster on NETLIB LP, 6% faster on MIPLIB relaxations +- Improve accuracy of dual simplex (refactor when optimal and primal residual is large with basis updates present) +- Reduce concurrent overhead for LP solves +- Reduce memory footprint of PDLP by around 50% +- Unify threading model across LP (concurrent mode) and MIP to use OpenMP task model to allow stricter control of the number of threads +- Unify Python `read` API on `Problem` for both MPS and LP file formats +- Build and test with CUDA 13.2 +- Build against OpenSSL3 in container and wheel for gRPC +- Substantially expand the in-repo skill set for AI coding agents (Copilot, Cline, Windsurf, Jules, Aider, Codex) covering developer onboarding, installation, numerical optimization, routing, and the server; add NVIDIA-signed skill cards, evaluation datasets, and a skill-evolution workflow under `skills/` + +### Bug Fixes (26.06) +- Fix a bug in MIP where the probing cache did not correctly update the model after each batch +- Fix a bug in MIP where loose tolerances in the objective function could lead to incorrect integrality detection +- Fix a bug in MIP where branch and bound nodes could be lost when a plunge exits early due to LP solve limits +- Fix a bug in MIP where the lower bound could be incorrect when using a single thread +- Fix a bug in LP/QP where dual variables and reduced costs could be incorrect when applying implied bounds to free variables +- Fix an issue in crossover where adding slack variables to a rank-deficient basis was accidentally O(rows^2) + +### Deprecated APIs (26.06) +- Python API: `readMPS` is deprecated +- C API: `cuOptCreateQuadraticProblem` and `cuOptCreateQuadraticRangedProblem` is deprecated +- LP batch mode is deprecated + +### Documentation (26.06) +- Refresh the contributor guide (`CONTRIBUTING.md`) with conda-environment recommendations and clarified test-suite scope +- Update the MIP scaling guide +- Migrate the support link to GitHub Discussions and tidy broken doc links + +### New Contributors (26.06) +- @np96 +- @srib +- @yuwenchen95 +- @Bubullzz +- @aycsi + +## Release Notes 26.04 + +### New Features (26.04) +- Run no-relaxation heuristics before presolve +- Add new MIP cuts: clique cuts and implied bounds cuts +- Add support for FP32 and mixed precision in PDLP +- Add option for using Batch PDLP in reliability branching +- Add UnboundedOrInfeasible termination status +- Expose settings for tuning heuristics +- Add support for Python 3.14 +- Add support for writing presolved model to a file +- gRPC based remote execution support on Python, C and CLI interface for LP/QP and MIP + +### Breaking Changes (26.04) +- The solved_by_pdlp field in the Python LP solution object was changed to solved_by +- Drop support for Python 3.10 + +### Improvements (26.04) +- Improve reliability branching by better ranking of unreliable variables +- Generate more MIR and Knapsack cuts +- Improve aggregation and complementation in MIR cuts +- Use variable lower and variable upper bounds in MIR cuts +- Improve numerics of mixed integer Gomory cuts +- Lift knapsack cuts +- Improve row and objective scaling for MIP +- Use objective function integrality when pruning node +- Reduce time for Markowitz factorization in dual simplex +- Reduce time for dual push inside crossover +- Reduce number of free variables in barrier +- Add gap information to primal heuristics logs when root relaxation is still solving +- Refactoring agentic skills to follow standard skill structure and add developer skills +- Adding skill to evolve skills + +### Bug Fixes (26.04) +- Fix a bug in LP/MIP where cuOpt reported incorrect termination status; we now correctly report UnboundedOrInfeasible +- Fix a bug in MIP where Papilo's probing presolver crashed; fix will be pushed upstream +- Fix a bug in MIP with a missing stream sync in the probing cache that was causing a crash +- Fix a bug in MIP leading to incorrect dual bound when nodes remain in the heap +- Fix a bug in MIP where nodes with objective less than the incumbent objective value were incorrectly fathomed +- Fix a bug in MIP where variables could violate their bounds in Feasibility Jump on the CPU +- Fix a bug in MIP where a race condition could occur when sharing solutions between branch and bound and heuristics +- Fix a bug in MIP where the solver was not respecting the time limit +- Fix a bug in MIP where the solver terminated at the end of the root relaxation solve +- Fix a bug in QP where quadratic terms were not written out to MPS files +- Fix a bug in MIP where cuOpt was taking a long time to terminate after optimal solution found +- Fix a bug in LP/barrier on problems containing variables with infinite lower bounds +- Fix a bug in MIP where batch PDLP for strong branching was running on the problem without cuts +- Fix a bug in Python API when using x + x*x, +x, -x expressions +- Update to the latest version of PSLP which includes bug fixes for incorrect infeasible classification + +### Documentation (26.04) +- Update docs to clarify the usage of getIncumbentValues() in the Python API + +## Release Notes 26.02 + +### New Features (26.02) + +- New parallel reliability branching inside MIP solver +- Mixed Integer Gomory, Mixed Integer Rounding, Knapsack and Strong Chvatal Gomory cuts are now added at root node +- Added an option to use batch PDLP when running strong branching at the root. Based on [Batched First-Order Methods for Parallel LP Solving in MIP](https://arxiv.org/abs/2601.21990) ([Nicolas Blin](https://github.com/Kh4ster), [Stefano Gualandi](https://github.com/stegua), [Christopher Maes](https://github.com/chris-maes), [Andrea Lodi](https://github.com/andrealodi), [Bartolomeo Stellato](https://github.com/bstellato)) +- Quadratic programming (QP) solver is now generally available (previously beta) +- New infeasibility detection option for PDLP's default solver mode Stable3 +- Solutions callbacks added to C API. Users can now retrieve the dual bound and pass in user data +- Multiple new diving techniques added for finding integer feasible solutions +- The [PSLP presolver](https://github.com/dance858/PSLP) is enabled by default for LP problems. Use the presolve option to select Papilo or disable +- Added a batch solve for routing to boost throughput for many similar instances +- Added experimental support for determinism in the parallel branch-and-bound solver. GPU heuristics are not supported yet in this mode + +### Breaking Changes (26.02) + +- The signatures of the solution callbacks have changed for the Python API +- To use PDLP warm start, presolve must now be explicitly disabled by setting `CUOPT_PRESOLVE=0`. Previously, presolve was disabled automatically + +### Improvements (26.02) + +- Improved primal/dual warm start for PDLP's default solver mode Stable3 +- Quadratic objectives can now be constructed via a matrix in Python API +- QP barrier now updates and solves augmented system on the GPU +- Improved performance for LP folding +- Probing implications and better variable ordering to strengthen presolve and branching +- Replace deprecated cuDF Column/Buffer APIs with pylibcudf and public cuDF interfaces +- Modernize dependency pinnings; make CUDA runtime linkage static for portability +- Build/tooling: add `--split-compile`, `--jobserver`, Clang host build, ThreadSanitizer, improved container scripts, and branch/commit metadata in images +- Use explicit `cudaStream_t` with `cub::DeviceTransform` and non-blocking streams for GPU control +- Enable barrier LP tests, add regression testing, and add SonarQube static analysis +- Added parameter for specifying the random seed used by the solver + +### Bug Fixes (26.02) + +- Fixed an issue with incorrect signs of dual variables and reduced costs on maximization problems +- Fix out-of-bounds in dense-column detection in barrier +- Correct infeasible-list handling to avoid incorrect infeasibility reports in dual simplex +- Fix race conditions found via Clang host build + ThreadSanitizer +- Resolve CUDA–Numba version mismatches with cuDF +- Fix device code to include required trailing return types +- Fix issue in crossover after dualization in barrier +- Repair container build and test failures +- Miscellaneous additional fixes and stability improvements + +### Documentation (26.02) + +- Update README and top-level docs for current build and usage +- Document new repository branching strategies and release-cycle details in README and CONTRIBUTING +- Add best practices for batch solving + +## Release Notes 25.12 + +### New Features (25.12) + +- New quadratic programming solver using the barrier method (currently in beta). +- Support for quadratic objectives added to the C and Python modeling APIs. +- LP concurrent mode now supports multiple GPUs. PDLP and barrier can now be run on separate GPUs. +- MIP root relaxation solves now use concurrent mode: PDLP, barrier, and dual simplex. + +### Improvements (25.12) + +- RINS heuristic adds a new improvement strategy in the MIP solver. +- Basis factorizations are now reused in the branch and bound tree. +- Improvement in propagating bounds from parent to child nodes in the branch and bound tree. +- GMRES with Cholesky/LDL preconditioning is now used for iterative refinement on QPs. +- Improved numerical stability of dual simplex when the basis is ill-conditioned. +- Improved robustness in barrier and PDLP: fixed cuSPARSE related leaks and added RAII-style wrappers for cuSPARSE structures. +- Papilo-based presolve carries over implied integer information from reductions, improving consistency of integrality handling. +- Build and CI workflows improved through assertion-default changes, better handling of git-hash rebuilds, and fixes to the nightly build matrix. +- Reduced package sizes by adjusting build outputs and test-only library linkage, and the TSP dataset download logic disables unneeded downloads. + +### Bug Fixes (25.12) + +- A crash in the incumbent test is resolved. +- Fixed memory leaks in Barrier and PDLP's cuSPARSE usage. +- The explored nodes in the MIP log now correctly reflects the actual nodes examined. +- A compilation issue in the solve_MIP benchmarking executable is fixed, restoring benchmark builds. +- A logger bug when log_to_console is false is fixed. +- Routing fixes improve TSP behavior when order locations are set. +- Nightly container testing and CI handling fix issues in the nightly container test suite and build jobs. +- A cuDF build_column deprecation issue fixed to keep compatibility with newer cuDF versions. + +### Documentation (25.12) + +- Missing parameters added to the documentation for LP and MILP. +- Release notes added to the main repository for easy access. +- Examples in the documentation improved. +- The openapi spec for the service showed the 'status' value for LP/MILP results as an int but it is actually a string. + +## Release Notes 25.10 + +### New Features (25.10) + +- New barrier method for solving LPs. Uses cuDSS for sparse Cholesky / LDT. +- Concurrent mode for LPs now uses PDLP, dual simplex, and barrier +- New PDLP solver mode Stable3. +- MIP presolve using Papilo (enabled by default). LP presolve using Papilo (optional). +- Parallel branch and bound on the CPU: multiple best-first search and diving threads + +### Breaking Changes (25.10) + +- New PDLP Solver mode Stable3 is the default + +### Improvements (25.10) + +- Add setting "CUOPT_BARRIER_DUAL_INITIAL_POINT" to change the dual initial point used by barrier +- CPUFJ for local search + simple rounding +- FP as a local search +- Sub-MIP recombiner and B&B global variable changes +- Implement GF(2) presolve reduction +- Implement node presolve +- CUDA 13/12.9 support +- Build and test with CUDA 13.0.0 +- Add read/write MPS and relaxation to python API +- Decompression for ``.mps.gz`` and ``.mps.bz2`` files +- Enable parallelism for root node presolve +- Enable singleton stuffing and use Papilo default params +- Make infeasibility checks consistent between the main solver and presolver +- Add maximization support for root node presolve +- Performance improvement in dual simplex's right-looking LU factorization +- Fix high GPU memory usage +- Print cuOpt version / machine info before solving +- ``cuopt-server``: update dependencies (drop httpx, add psutil) +- Add nightly testing of cuOpt jump interface +- Compression tests are not run when compression is disabled +- Add sanitizer build option- Heuristic Improvements: balance between generation and improvement heuristics +- Loosen presolve tolerance and update timers to report cumulative presolve/solve time +- Warn in case a dependent library is not found in libcuopt load +- Combined variable bounds +- Add Commit Sha to container for reference +- use GCC 14, consolidate dependency groups, update pre-commit hooks +- Add support for nightly ``cuopt-examples`` notebook testing +- Reduce hard-coded version usage in repo +- Container to work on all different users including root +- Changes to download LP and MILP datasets, and also disable cvxpy testing for 3.10 +- Faster engine compile time +- Fix pre-commit for trailing whitespace and end of file +- Merge update version and fix version format bugs +- This library now supports the QPS format, which is an extension of the standard MPS format for representing quadratic programming problems. + +### Bug Fixes (25.10) + +- Fix variables out of bounds caused by CPUFJ LP scratch thread +- Fix the maybe-uninitialized compilation error +- Fix linking errors in the test suite when disabling C adaptor +- Compute relative gap with respect to user objectives +- Add http timeout values for general, send, and receive to client +- Fix bug in ``fixed_problem_computation`` +- Remove ``limiting_resource_adaptor`` leftover +- Add support for cuda13 container and fix cuda13 lib issues in wheel +- Return Infeasible if the user problem contains crossing bounds +- Fix out-of-bound access in ``clean_up_infeasibilities`` +- Empty columns with infinite bounds are not removed + +### Documentation (25.10) + +- Add tutorial video links to Decompression +- Add warmstart, model update, update docs +- add docs on CI workflow inputs +- Add name to drop-down for video link +- Add video link to the docs and to the Readme +- Add documentation on nightly installation commands +- Fix version in version tab, change log, and fix typos +- Doc update for container version update, and add ``nvidia-cuda-runtime`` as a dependency + +## Release Notes 25.08 + +### New Features (25.08) + +- Added Python API for LP and MILP ([#223](https://github.com/NVIDIA/cuopt/pull/223)) + +### Breaking Changes (25.08) + +- Fixed versioning for nightly and release package ([#175](https://github.com/NVIDIA/cuopt/pull/175)) + +### Improvements (25.08) + +- New heuristic improvements ([#178](https://github.com/NVIDIA/cuopt/pull/178)) +- Add helm chart for cuOpt service ([#224](https://github.com/NVIDIA/cuopt/pull/224)) +- Add nightly container support ([#180](https://github.com/NVIDIA/cuopt/pull/180)) +- Adding deb package support as a beta feature ([#190](https://github.com/NVIDIA/cuopt/pull/190)) +- Use cusparsespmv_preprocess() now that Raft implements it ([#120](https://github.com/NVIDIA/cuopt/pull/120)) +- Create a bash script to run MPS files in parallel ([#87](https://github.com/NVIDIA/cuopt/pull/87)) +- Several fixes needed to compile cuOpt with LLVM ([#121](https://github.com/NVIDIA/cuopt/pull/121)) +- Small fixes for corner cases ([#130](https://github.com/NVIDIA/cuopt/pull/130)) +- Small improvements on how paths are handled in tests ([#129](https://github.com/NVIDIA/cuopt/pull/129)) +- Update cxxopts to v3.3.1 ([#128](https://github.com/NVIDIA/cuopt/pull/128)) +- Bump actions/checkout in nightly.yaml to v4 ([#230](https://github.com/NVIDIA/cuopt/pull/230)) +- Remove CUDA 11 specific changes from repo ([#222](https://github.com/NVIDIA/cuopt/pull/222)) +- Heuristic improvements with solution hash, MAB and simplex root solution ([#216](https://github.com/NVIDIA/cuopt/pull/216)) +- Various typos in comments and strings, note on result dir ([#200](https://github.com/NVIDIA/cuopt/pull/200)) +- Split very large tests into smaller individual test cases ([#152](https://github.com/NVIDIA/cuopt/pull/152)) +- Fix compile error when using clang with C++20 ([#145](https://github.com/NVIDIA/cuopt/pull/145)) +- Relax pinnings on several dependencies, remove nvidia channel ([#125](https://github.com/NVIDIA/cuopt/pull/125)) +- Fix compile error when building with clang ([#119](https://github.com/NVIDIA/cuopt/pull/119)) +- cuOpt service add healthcheck for / ([#114](https://github.com/NVIDIA/cuopt/pull/114)) +- refactor(shellcheck): fix all remaining shellcheck errors/warnings ([#99](https://github.com/NVIDIA/cuopt/pull/99)) +- Add CTK 12.9 fatbin flags to maintain existing binary sizes ([#58](https://github.com/NVIDIA/cuopt/pull/58)) + +### Bug Fixes (25.08) + +- Fixed a segfault on bnatt500 due to small mu leading to inf/nan ([#254](https://github.com/NVIDIA/cuopt/pull/254)) +- Fixed a bug in basis repair. Recover from numerical issues in primal update ([#249](https://github.com/NVIDIA/cuopt/pull/249)) +- Unset NDEBUG in cmake in assert mode ([#248](https://github.com/NVIDIA/cuopt/pull/248)) +- Manual cuda graph creation in load balanced bounds presolve ([#242](https://github.com/NVIDIA/cuopt/pull/242)) +- Fixed bug on initial solution size in the check and cuda set device order ([#226](https://github.com/NVIDIA/cuopt/pull/226)) +- Disable cuda graph in batched PDLP ([#225](https://github.com/NVIDIA/cuopt/pull/225)) +- Fix logging levels format with timestamps ([#201](https://github.com/NVIDIA/cuopt/pull/201)) +- Fix bug in scaling of dual slacks and sign of dual variables for >= constraints ([#191](https://github.com/NVIDIA/cuopt/pull/191)) +- Fix inversion crossover bug with PDP and prize collection ([#179](https://github.com/NVIDIA/cuopt/pull/179)) +- Fix a bug in extract_best_per_route kernel ([#156](https://github.com/NVIDIA/cuopt/pull/156)) +- Fix several bugs appeared in unit testing of JuMP interface ([#149](https://github.com/NVIDIA/cuopt/pull/149)) +- Fix incorrect reported solving time ([#131](https://github.com/NVIDIA/cuopt/pull/131)) +- Fix max offset ([#113](https://github.com/NVIDIA/cuopt/pull/113)) +- Fix batch graph capture issue caused by pinned memory allocator ([#110](https://github.com/NVIDIA/cuopt/pull/110)) +- Fix bug in optimization_problem_solution_t::copy_from ([#109](https://github.com/NVIDIA/cuopt/pull/109)) +- Fix issue when problem has an empty problem in PDLP ([#107](https://github.com/NVIDIA/cuopt/pull/107)) +- Fix crash on models with variables but no constraints ([#105](https://github.com/NVIDIA/cuopt/pull/105)) +- Fix inversion of constraint bounds in conditional bounds presolve ([#75](https://github.com/NVIDIA/cuopt/pull/75)) +- Fix data initialization in create depot node for max travel time feature ([#74](https://github.com/NVIDIA/cuopt/pull/74)) + +### Documentation (25.08) + +- Added more pre-commit checks to ensure coding standards ([#213](https://github.com/NVIDIA/cuopt/pull/213)) +- Mention GAMS and GAMSPy in third-party modeling languages page in documentation ([#206](https://github.com/NVIDIA/cuopt/pull/206)) +- Enable doc build workflow and build script for PR and Nightly ([#203](https://github.com/NVIDIA/cuopt/pull/203)) +- Fix the link to Python docs in README ([#118](https://github.com/NVIDIA/cuopt/pull/118)) +- Add link checker for doc build and test ([#229](https://github.com/NVIDIA/cuopt/pull/229)) + +## Release Notes 25.05 + +### New Features (25.05) + +- Added concurrent mode that runs PDLP and Dual Simplex together +- Added crossover from PDLP to Dual Simplex +- Added a C API for LP and MILP +- PDLP: Faster iterations and new more robust default PDLPSolverMode Stable2 +- Added support for writing out mps file containing user problem. Useful for debugging + +### Breaking Changes (25.05) + +- NoTermination is now a NumericalError +- Split cuOpt as libcuopt and cuopt wheel + +### Improvements (25.05) + +- Hook up MILP Gap parameters and add info about number of nodes explored and simplex iterations +- FJ bug fixes, tests and improvements +- Allow no time limit in MILP +- Refactor routing +- Probing cache optimization +- Diversity improvements for routing +- Enable more compile warnings and faster compile by bypassing rapids fetch +- Constraint prop based on load balanced bounds update +- Logger file handling and bug fixes on MILP +- Add shellcheck to pre-commit and fix warnings + +### Bug Fixes (25.05) + +- In the solution, ``termination_status`` should be cast to correct enum. +- Fixed a bug using vehicle IDs in construct feasible solution algorithm. +- FP recombiner probing bug fix. +- Fix concurrent LP crashes. +- Fix print relative dual residual. +- Handle empty problems gracefully. +- Improve breaks to allow dimensions at arbitrary places in the route. +- Free var elimination with a substitute variable for each free variable. +- Fixed race condition when resetting vehicle IDs in heterogenous mode. +- cuOpt self-hosted client, some MILPs do not have all fields in ``lp_stats``. +- Fixed RAPIDS logger usage. +- Handle LP state more cleanly, per solution. +- Fixed routing solver intermittent failures. +- Gracefully exit when the problem is infeasible after presolve. +- Fixed bug on dual resizing. +- Fix occasional incorrect solution bound on maximization problems +- Fix inversion of constraint bounds in conditional bounds presolve +- Pdlp fix batch cuda graph +- Fix obj constant on max. Fix undefined memory access at root +- Allow long client version in service version check, this fixes the issue in case version is of the format 25.05.00.dev0 + +### Documentation (25.05) + +- Restructure documentation to accommodate new APIs diff --git a/fern/docs/pages/resources.mdx b/fern/docs/pages/resources.mdx new file mode 100644 index 0000000000..1de6f15fab --- /dev/null +++ b/fern/docs/pages/resources.mdx @@ -0,0 +1,29 @@ +--- +title: "Resources" +--- + +## [Sample Notebooks](https://github.com/NVIDIA/cuopt-examples) + +## cuOpt Examples and Tutorials Videos + + + + - [Quick Start to GPU-Accelerated Large-Scale Logistics and Supply Chain Optimization with NVIDIA cuOpt](https://www.youtube.com/watch?v=CVtqcUmW9nU) + - [Accelerated MILP for Supply Chain, Logistics & Planning Optimization — Quick Start with NVIDIA cuOpt](https://www.youtube.com/watch?v=jnDPQeUO0CM) + - [Solving Vehicle Routing Problems — Hands-On with Open Source NVIDIA cuOpt](https://www.youtube.com/watch?v=kSATyJrELUI) + + + +## [Test cuOpt with NVIDIA Launchable](https://brev.nvidia.com/launchable/deploy?launchableID=env-2qIG6yjGKDtdMSjXHcuZX12mDNJ) + +## [Test cuOpt on Google Colab](https://colab.research.google.com/github/nvidia/cuopt-examples/) + +Please note that you need to choose a Runtime as GPU in order to run the notebooks. + +## [File a Bug](https://github.com/NVIDIA/cuopt/issues/new?template=bug_report.md) + +## [Ask a Question](https://github.com/NVIDIA/cuopt/issues/new?template=submit-question.md) + +## [Blogs](https://developer.nvidia.com/blog/recent-posts/?products=cuOpt) + +## Contact us - [cuopt@nvidia.com](mailto:cuopt@nvidia.com) diff --git a/fern/docs/pages/routing-features.mdx b/fern/docs/pages/routing-features.mdx new file mode 100644 index 0000000000..3c2fde35c1 --- /dev/null +++ b/fern/docs/pages/routing-features.mdx @@ -0,0 +1,106 @@ +--- +title: "Routing Features" +--- + +## Availability + +The Routing solver is available in two forms: + + - **As a Self-Hosted Service**: cuOpt Routing can be deployed as a service in your own infrastructure, enabling you to maintain full control while integrating it into your existing systems. For more information, see [Quick Start](cuopt-server/quick-start). + - **Python SDK**: A Python package that provides direct access to cuOpt's routing capabilities through a simple, intuitive API. This allows for seamless integration into Python applications and workflows. For more information, see [Quick Start](cuopt-python/quick-start). + +Both options provide the same routing optimization capabilities while offering flexibility in deployment and integration approaches. + +## Batch Solving (TSP) + +The Python API supports solving multiple TSP (or routing) instances in a single call via **batch mode**. Use `cuopt.routing.BatchSolve` with a list of `cuopt.routing.DataModel` objects to run many problems in parallel and improve throughput. See [Routing Examples](cuopt-python/routing/routing-examples) for details and an example. + +## Heterogeneous Fleet + +In a Vehicle Routing Problem (VRP), enterprise customers may have a fleet that is composed of different vehicles, such as trucks and motorbikes. Each vehicle type will likely have different costs, travel times, different constraints such as time windows, start and end locations, and carrying capacity. To model such scenarios, users can provide multiple input matrices (travel time matrix/cost), one for each type. Constraints must be provided for each vehicle. + +## Multiple Input Matrices + +A cost matrix needs to be a square matrix of some travel metric that is passed to the NVIDIA cuOpt solver. In many variations of the Traveling Salesman Problem (TSP), this includes travel time, distance, or another business cost. A cost matrix is a square matrix that represents the cost of traveling between each two pairs of locations in the problem. `cost matrix` can hold any cost matrix; the cost can be travel time, travel distance, dollar cost, or any weighted function of cost. If the cost matrix doesn't use travel time, `travel time matrix` should be used so that the cost matrix is used for optimization, while the travel time matrix is used for meeting time constraints. + +In the case of a heterogeneous fleet, different vehicle types may have different travel times. To support this, multiple `cost matrix` and `travel time matrix` can be provided such that each vehicle type has a corresponding matrix. + +## Multiple Input Waypoint Graphs + +Similar to cost matrix, distance and time details can be provided through waypoint graphs through `cost waypoint graph` or `travel time waypoint graph`. Each vehicle type will have its own waypoint graph data for both cost and time. + +## Vehicle Time Windows + +Time windows represent the operating time of the vehicles. There is one time window per vehicle. This data is represented as an integer. Raw data may include Universal Time Stamp (UTC) date/time format or string format which must be converted to floating value. (Example: 9:00 am - 6:00 pm converted to minutes in a 24-hour period starting at 12:00 am, would be `[540, 1080]`). All time/cost units provided to the cuOpt solver should be in the same unit. If the travel time cost matrix includes the travel time in between locations in minutes, the time windows must be integer representation of minutes as well. + + +All time windows are expected to be =\> 0, and all time windows are int32 types. So the time window range supported would be `[0, 2^31-1]`; this translates to `[January 1, 1970, 00:00:00, January 19, 2038, 03:14:07]` in UTC. + + +## Vehicle Breaks + +In addition to vehicle time windows, vehicles may also have break time windows. If the time windows represent a driver's working hours in a day, the break time window may represent their lunch break in the middle of the day. The integer representation is consistent with the time windows. In case raw input data is in UTC timestamp, if a vehicle is working a shift of 9:00 am - 6:00 pm, then in a 24-hour period this is equivalent to `540 - 1080`. If the vehicle has a break from 1:00 pm - 1:30 pm, then the break time window would be `[780, 810]`. + +Along with time, users can also specify possible break locations using `vehicle break locations`. + +There are two types of breaks, + + - Uniform breaks - Where each driver in the fleet has same number breaks that they can avail. Like 3 breaks in a shift (coffee, lunch, coffee). This is achieved through `vehicle break time windows` and `vehicle break durations`. + - Non-Uniform breaks - Where each driver might need to take different set of breaks, may be someone called-in sick or some set of workers need more breaks since they work extra hours. This is done through `vehicle breaks`. + +Only one of the type of breaks can be used at a time. + +## Prize Collection + +The *prizes* may be used to set a prize for each task. This supports cases where all tasks are not feasible or where-in servicing all tasks are not as profitable as dropping some low prize tasks and servicing the rest and that the user requires tasks with the highest prize to be considered before trying other tasks. If the cost of serving a customer is too high compared to the prize of serving a customer, the solver will drop that customer to improve overall cost. + +If the primary goal is to serve as many customers as possible, it is advisable to set very high prizes. Prize collection would be an effective way to drop infeasible tasks and prioritizing tasks with higher prizes. The main difference is in objective function, the total prize will be negated rather than getting added to overall cost. If the objective weight corresponding to prize objective is set to zero, the prize collection mechanism is disabled. + +## Objectives + +The default objective of the solver is to optimize the cost computed using cost matrices. However, a few other objectives can be set. The final objective is the linear combination of all the objectives specified. The specified objective weights are used as coefficients. By default, the cuOpt solver optimizes for vehicle count first and final objective next. + +## Drop First / Last Trips + +By dropping the first or last trip, the cuOpt solver does not take into account the vehicles' trip from their depots to their first stop, or the trip from their last stop back to the depots. With these parameters, the route includes only travel costs and times between task locations. In cases where drivers may start their shift from their home location instead of an assigned depot, the trip to and from the depot is unnecessary. + +## Pickup and Deliveries + +Some use cases may include picking up an order from one location and delivering it to another. Each order has two corresponding locations: one for pickup and one for delivery. The same vehicle must handle both the pickup and delivery of the same order, and the pickup of the order must occur prior to the delivery. + +## Precise Time Limits + +It is recommended to use the default time limit (which is `10 + number_of_nodes/6`) seconds. If the time limit is set to X seconds, then solver continues to run for X seconds even when there is no improvement in the solution quality. + + +In case of self-hosted version, the `time_limit` set is what solver will essentially use to solve the problem and doesn't include `network transfer`, `etl`, `validation of input`, `instance being busy with other requests` and few other overheads, these overheads would be comparatively smaller. So the overall request to response round trip time would be `solve_time` + `overhead`. + + +## Vehicle Start and End Locations + +Each vehicle in the fleet must have a start and end location for the given set of locations in a waypoint graph or cost matrix. These locations must be included in the cost matrices or waypoint graphs. In many use cases, these start and end locations are depots or distribution centers, such that vehicles depart their assigned depot in the morning, fulfill all of their assigned tasks, and then return to the assigned depot at the end of the work day. The start and end locations are not necessarily the same location (e.g., a vehicle departs from depot 1 in the morning but returns to depot 2 at night). + +## Minimum Constraint on Number of Vehicles + +By default, cuOpt tries to minimize the number of vehicles used in the solution and considers the fleet size an upper bound. If a given input fleet has 20 vehicles available but only 10 are needed to fulfill all of the tasks, then the cuOpt solution will include only 10 vehicles. To set a lower bound on the number of vehicles used, set `min_vehicles` in `fleet_data`. If the exact number of vehicles to be used is known, specifying a fleet of the desired size and setting `min vehicles` equal to the fleet size will guarantee that all vehicles are used. + +## Maximum Constraints per Vehicle + +Vehicles may have a constraint for maximum distance each vehicle can travel or maximum time a vehicle can operate. This means that even if a vehicle has a time window of 9am to 9pm, and a driver may be available to work for those 12 hours, we can add a constraint that a work day must not exceed 8 of those 12 hours. + +## Fixed Cost per Vehicle + +Vehicles can have different fixed costs associated with them. This helps in scenarios where a single vehicle with a higher cost can be avoided if it can be done with two or more vehicles with lesser costs. This would be dependent on the objective function. + +## Mapping Orders to Vehicles, and Vehicles to Orders + +By default, cuOpt will assign orders to vehicles based on the optimal routes. However, in some cases, it makes sense to assign specific orders to specific vehicles, or, conversely, specific vehicles to specific orders. + + - `order vehicle match` allows assigning orders to vehicles. For example, a food distribution center wants to make shipments to grocery stores around the city. Say the fleet consists of refrigerated trucks, such that they can carry frozen food, and vans, which cannot. In this case, we want to assign the orders that contain frozen food to the trucks (rather than just any vehicle). + - `vehicle order match` allows assigning vehicles to orders. For example, a maintenance company can have many employees (technicians) who can fulfil various tasks. When a customer requests a service, the company may dispatch any available technician to fulfill their request. However, if a customers request a service that only one technician can fulfill, those orders can be assigned to this one technician. + +In cases where a set of orders need to be assigned to a set of vehicles, either constraint can be used as long as the mapping is done correctly. + +## Initial Solution + +Previously run solutions or uploaded solutions can be used as an initial solution to start a new request to boost the speed to the solution. [Examples](#initial-solution-in-python) are shared on the self-hosted page. diff --git a/fern/docs/pages/system-requirements.mdx b/fern/docs/pages/system-requirements.mdx new file mode 100644 index 0000000000..e9e015699c --- /dev/null +++ b/fern/docs/pages/system-requirements.mdx @@ -0,0 +1,128 @@ +--- +title: "System Requirements" +--- + +Dependencies are installed automatically when using the pip and Conda installation methods. However, users would still need to make sure the system meets the minimum requirements. + + + + - - System Architecture: + + - x86-64 + - ARM64 + + - - GPU: + + - Volta architecture or better (Compute Capability \>=7.0) + + - - CPU: + + - 4+ cores + + - - System Memory: + + - 16+ GB RAM + + - - NVMe SSD Storage: + + - 100+ GB free space + + - - CUDA: + + - 12.0+ + + - - Python: + + - \>= 3.11.\* and <= 3.14.\* + + - - OpenSSL: + + - 3.x runtime (`libssl.so.3` and `libcrypto.so.3`) must be present on the host + - Shipped by default on Ubuntu 22.04+, Debian 12+, RHEL/Rocky/Alma 9+, Fedora 36+ + - On RHEL/Rocky/Alma 8, install the `openssl3` package from EPEL (see [Faq](faq)) + - On Ubuntu 20.04, install OpenSSL 3 from a PPA/backport or use the cuOpt container + + - - NVIDIA drivers: + + - 525.60.13+ (Linux) + - 527.41+ (Windows) + + - - OS: + + - - Linux distributions with glibc\>=2.28 (released in August 2018): + + - Arch Linux (minimum version 2018-08-02) + - Debian (minimum version 10.0) + - Fedora (minimum version 29) + - Linux Mint (minimum version 20) + - Rocky Linux / Alma Linux / RHEL (minimum version 8) + - Ubuntu (minimum version 20.04) + - Windows 11 with WSL2 + + - - CUDA & NVIDIA Driver combinations: + + - CUDA 12.0 with Driver 525.60.13+ + - CUDA 12.2 with Driver 535.86.10+ + - CUDA 12.5 with Driver 555.42.06+ + - CUDA 12.9 with Driver 570.42.01+ + - CUDA 13.0 with Driver 580.65.06+ + + + + + + - - System Architecture: + + - x86-64 + - ARM64 + + - - GPU: + + - NVIDIA H100 SXM (compute capability \>= 9.0) + + - - CPU: + + - 32+ cores + + - - System Memory: + + - 64+ GB RAM + + - - NVMe SSD Storage: + + - 100+ GB free space + + - - CUDA: + + - 13.0 + + - Latest NVIDIA drivers (580.65.06+) + + - - OS: + + - - Linux distributions with glibc\>=2.28 (released in August 2018): + + - Arch Linux (minimum version 2018-08-02) + - Debian (minimum version 10.0) + - Fedora (minimum version 29) + - Linux Mint (minimum version 20) + - Rocky Linux / Alma Linux / RHEL (minimum version 8) + + + +The above configuration will provide optimal performance for large-scale optimization problems. + +## Container + + - [nvidia-container-toolkit](https://docs.nvidia.com/ai-enterprise/deployment/vmware/latest/docker.html) needs to be installed + +## Thin-client for Self-Hosted + + - OS: Linux + + - - System Architecture: + + - x86-64 + - ARM64 + + - Python \>= 3.11.x <= 3.14.x diff --git a/fern/docs/pages/thirdparty_modeling_languages/index.mdx b/fern/docs/pages/thirdparty_modeling_languages/index.mdx new file mode 100644 index 0000000000..cda0157cdc --- /dev/null +++ b/fern/docs/pages/thirdparty_modeling_languages/index.mdx @@ -0,0 +1,27 @@ +--- +title: "Third-Party Modeling Languages" +--- + +## AMPL Support + +AMPL can be used with near zero code changes: simply switch to cuOpt as a solver to solve linear and mixed-integer programming problems. Please refer to the [AMPL documentation](https://www.ampl.com/) for more information. Also, see the example notebook in the [colab](https://colab.research.google.com/drive/1eEQik_pae4g_tJQ61QJFlO1fFBXazpBr?usp=sharing). + +## GAMS and GAMSPy Support + +GAMS and GAMSPy models can be used with near zero code changes after setting up the solver link: simply switch to cuOpt as a solver to solve linear and mixed-integer programming problems (e.g. `gams trnsport lp=cuopt`). Please refer to the [GAMS cuOpt link repository](https://github.com/GAMS-dev/cuoptlink-builder) for more information on how to setup GAMS and GAMSPy for cuOpt. Also, see the example notebook in the [cuopt-examples](https://github.com/NVIDIA/cuopt-examples) repository. + +## PuLP Support + +PuLP can be used with near zero code changes: simply switch to cuOpt as a solver to solve linear and mixed-integer programming problems. Please refer to the [PuLP documentation](https://pypi.org/project/PuLP/) for more information. Also, see the example notebook in the [cuopt-examples](https://github.com/NVIDIA/cuopt-examples) repository. + +## JuMP Support + +JuMP can be used with near zero code changes: simply switch to cuOpt as a solver to solve linear and mixed-integer programming problems. Please refer to the [JuMP documentation](https://github.com/jump-dev/cuOpt.jl) for more information. + +## Pyomo Support + +Pyomo models can be used with near zero code changes via cuOpt's direct solver interface: simply select cuOpt as the solver to solve linear and mixed-integer programming problems. Please refer to the [Pyomo documentation](https://www.pyomo.org/) for more information. + +## CVXPY Support + +CVXPY can be used with near zero code changes: simply select cuOpt as the solver to solve linear and quadratic programs, as well as QCQP/SOCP problems (beta). Please refer to the [CVXPY documentation](https://www.cvxpy.org/) for more information. diff --git a/fern/docs/pages/transition.mdx b/fern/docs/pages/transition.mdx new file mode 100644 index 0000000000..c5d1753b17 --- /dev/null +++ b/fern/docs/pages/transition.mdx @@ -0,0 +1,76 @@ +--- +title: "Transition Guide for Change in Features" +--- + +In addition to the quality improvements, some new features were added, and some features were deprecated to improve user experience. For any questions, please reach out to the cuOpt team through github issues. + +Parameter/option statuses are listed below, they express how each of these options are affected: + +> **New** - A new feature has been added. +> +> **Update** - A change in definition of feature. +> +> **Deprecated** - These options will be accepted but will be removed in the future. In the case of the cuOpt service, the server will also return a warning noting that a feature is deprecated. +> +> **Limited** - These options are limited with respect to the number of dimensions that can be provided. +> +> **Removed** - These features were deprecated in a previous release and completely removed in this one. + +For all solver\_configs fields, see the [Convex Settings](convex-settings) and [Mip Settings](mip-settings) guides or the service openapi spec [Open Api](open-api). + +## Changes to solver\_configs.tolerances + +The following fields are **Deprecated** in `solver_configs.tolerances` for the service: + + - absolute\_primal + - absolute\_dual + - absolute\_gap + - relative\_primal + - relative\_dual + - relative\_gap + - primal\_infeasible + - dual\_infeasible + - integrality\_tolerance + - absolute\_mip\_gap + - relative\_mip\_gap + +The following fields are **New** in `solver_configs.tolerances` for the service and replace the deprecated fields above: + + - absolute\_primal\_tolerance + - absolute\_dual\_tolerance + - absolute\_gap\_tolerance + - relative\_primal\_tolerance + - relative\_dual\_tolerance + - relative\_gap\_tolerance + - primal\_infeasible\_tolerance + - dual\_infeasible\_tolerance + - mip\_integrality\_tolerance + - mip\_absolute\_gap + - mip\_relative\_gap + +The following fields are **New** in `solver_configs.tolerances` for the service but were available in the C API in 25.05 + + - mip\_absolute\_tolerance + - mip\_relative\_tolerance + +## Changes to solver\_configs + +The following fields are **Deprecated** in `solver_configs` for the service: + + - solver\_mode + - heuristics\_only + +The following fields are **New** in `solver_configs` for the service and replace the deprecated fields above: + + - pdlp\_solver\_mode + - mip\_heuristics\_only + +The following are **New** in `solver_configs` for the service but were available in the C API in 25.05 + + - strict\_infeasibility + - user\_problem\_file + - per\_constraint\_residual + - save\_best\_primal\_so\_far + - first\_primal\_feasible + - log\_file + - solution\_file diff --git a/fern/docs/pages/versions/v25-05.mdx b/fern/docs/pages/versions/v25-05.mdx new file mode 100644 index 0000000000..ddb96f63dc --- /dev/null +++ b/fern/docs/pages/versions/v25-05.mdx @@ -0,0 +1,7 @@ +--- +title: "cuOpt 25.05 Documentation (Archived)" +--- + + + +Redirecting to [cuOpt 25.05 archived documentation](https://archive.docs.nvidia.com/cuopt/user-guide/25.05.00/)... diff --git a/fern/docs/pages/versions/v25-08.mdx b/fern/docs/pages/versions/v25-08.mdx new file mode 100644 index 0000000000..243b363abe --- /dev/null +++ b/fern/docs/pages/versions/v25-08.mdx @@ -0,0 +1,7 @@ +--- +title: "cuOpt 25.08 Documentation (Archived)" +--- + + + +Redirecting to [cuOpt 25.08 archived documentation](https://archive.docs.nvidia.com/cuopt/user-guide/25.08.00/)... diff --git a/fern/docs/pages/versions/v25-12.mdx b/fern/docs/pages/versions/v25-12.mdx new file mode 100644 index 0000000000..df639ca049 --- /dev/null +++ b/fern/docs/pages/versions/v25-12.mdx @@ -0,0 +1,7 @@ +--- +title: "cuOpt 25.12 Documentation (Archived)" +--- + + + +Redirecting to [cuOpt 25.12 archived documentation](https://archive.docs.nvidia.com/cuopt/user-guide/25.12.00/)... diff --git a/fern/docs/pages/versions/v26-02.mdx b/fern/docs/pages/versions/v26-02.mdx new file mode 100644 index 0000000000..21c488b144 --- /dev/null +++ b/fern/docs/pages/versions/v26-02.mdx @@ -0,0 +1,7 @@ +--- +title: "cuOpt 26.02 Documentation (Archived)" +--- + + + +Redirecting to [cuOpt 26.02 archived documentation](https://archive.docs.nvidia.com/cuopt/user-guide/26.02.00/)... diff --git a/fern/docs/pages/versions/v26-04.mdx b/fern/docs/pages/versions/v26-04.mdx new file mode 100644 index 0000000000..30940114b1 --- /dev/null +++ b/fern/docs/pages/versions/v26-04.mdx @@ -0,0 +1,7 @@ +--- +title: "cuOpt 26.04 Documentation (Archived)" +--- + + + +Redirecting to [cuOpt 26.04 archived documentation](https://archive.docs.nvidia.com/cuopt/user-guide/26.04.00/)... diff --git a/fern/docs/pages/versions/v26-06.mdx b/fern/docs/pages/versions/v26-06.mdx new file mode 100644 index 0000000000..e8170c1e39 --- /dev/null +++ b/fern/docs/pages/versions/v26-06.mdx @@ -0,0 +1,7 @@ +--- +title: "cuOpt 26.06 Documentation (Archived)" +--- + + + +Redirecting to [cuOpt 26.06 archived documentation](https://archive.docs.nvidia.com/cuopt/user-guide/26.06.00/)... diff --git a/fern/docs/scripts/cuopt-install-version.js b/fern/docs/scripts/cuopt-install-version.js new file mode 100644 index 0000000000..f9b8db807b --- /dev/null +++ b/fern/docs/scripts/cuopt-install-version.js @@ -0,0 +1 @@ +window.CUOPT_INSTALL_VERSION = {"conda": "26.08", "pip": "26.8"}; diff --git a/docs/cuopt/source/_static/install-selector.css b/fern/docs/scripts/install-selector.css similarity index 100% rename from docs/cuopt/source/_static/install-selector.css rename to fern/docs/scripts/install-selector.css diff --git a/fern/docs/scripts/install-selector.js b/fern/docs/scripts/install-selector.js new file mode 100644 index 0000000000..18bc23b51f --- /dev/null +++ b/fern/docs/scripts/install-selector.js @@ -0,0 +1,398 @@ +/** + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * cuOpt install selector - generates install commands from user choices. + * Stable version comes from window.CUOPT_INSTALL_VERSION (injected at build from cuopt.__version__). + * Next = stable + 2 on minor with year rollover (YY.MM format). Update buildCommands() when commands change. + */ +(function () { + "use strict"; + + /* Version constants — resolved lazily so this script works even if cuopt-install-version.js + loads after us (both are injected async by Next.js's Script component). */ + var V_CONDA = null, V = null, V_CONDA_NEXT = null, V_NEXT = null; + + function resolveVersion() { + if (V_CONDA) return true; + var ver = typeof window !== "undefined" && window.CUOPT_INSTALL_VERSION; + if (!ver || !ver.conda || !ver.pip) return false; + V_CONDA = ver.conda; + V = ver.pip; + var parts = V_CONDA.split("."); + var major = parseInt(parts[0], 10); + var minor = parseInt(parts[1], 10) || 0; + var nextMinor = minor + 2; + var nextMajor = major; + if (nextMinor > 12) { nextMajor = major + 1; nextMinor = nextMinor - 12; } + V_CONDA_NEXT = nextMajor + "." + (nextMinor < 10 ? "0" : "") + nextMinor; + V_NEXT = nextMajor + "." + nextMinor; + return true; + } + + function buildCommands() { + var containerLib = { + stable: { + cu12: { + default: "docker pull nvidia/cuopt:latest-cu12", + run: "docker run --gpus all -it --rm nvidia/cuopt:latest-cu12 /bin/bash", + }, + cu13: { + default: "docker pull nvidia/cuopt:latest-cu13", + run: "docker run --gpus all -it --rm nvidia/cuopt:latest-cu13 /bin/bash", + }, + "cu13-ubi10": { + default: "docker pull nvidia/cuopt:latest-cu13-ubi10", + run: "docker run --gpus all -it --rm nvidia/cuopt:latest-cu13-ubi10 /bin/bash", + }, + }, + nightly: { + cu12: { + default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu12", + run: "docker run --gpus all -it --rm nvidia/cuopt:" + V_NEXT + ".0a-cu12 /bin/bash", + }, + cu13: { + default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu13", + run: "docker run --gpus all -it --rm nvidia/cuopt:" + V_NEXT + ".0a-cu13 /bin/bash", + }, + "cu13-ubi10": { + default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu13-ubi10", + run: "docker run --gpus all -it --rm nvidia/cuopt:" + V_NEXT + ".0a-cu13-ubi10 /bin/bash", + }, + }, + }; + + return { + python: { + pip: { + stable: { + cu12: "pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==" + V + ".*'", + cu13: "pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu13==" + V + ".*'", + }, + nightly: { + cu12: "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'cuopt-cu12==" + V_NEXT + ".*'", + cu13: "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'cuopt-cu13==" + V_NEXT + ".*'", + }, + }, + conda: { + stable: { + cu12: "conda install -c rapidsai -c conda-forge -c nvidia cuopt=" + V_CONDA + ".* cuda-version=12.9", + cu13: "conda install -c rapidsai -c conda-forge -c nvidia cuopt=" + V_CONDA + ".* cuda-version=13.0", + }, + nightly: { + cu12: "conda install -c rapidsai-nightly -c conda-forge -c nvidia cuopt=" + V_CONDA_NEXT + ".* cuda-version=12.9", + cu13: "conda install -c rapidsai-nightly -c conda-forge -c nvidia cuopt=" + V_CONDA_NEXT + ".* cuda-version=13.0", + }, + }, + container: containerLib, + }, + c: { + pip: { + stable: { + cu12: "pip install --extra-index-url=https://pypi.nvidia.com 'libcuopt-cu12==" + V + ".*'", + cu13: "pip install --extra-index-url=https://pypi.nvidia.com 'libcuopt-cu13==" + V + ".*'", + }, + nightly: { + cu12: "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'libcuopt-cu12==" + V_NEXT + ".*'", + cu13: "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'libcuopt-cu13==" + V_NEXT + ".*'", + }, + }, + conda: { + stable: { + cu12: "conda install -c rapidsai -c conda-forge -c nvidia libcuopt=" + V_CONDA + ".* cuda-version=12.9", + cu13: "conda install -c rapidsai -c conda-forge -c nvidia libcuopt=" + V_CONDA + ".* cuda-version=13.0", + }, + nightly: { + cu12: "conda install -c rapidsai-nightly -c conda-forge -c nvidia libcuopt=" + V_CONDA_NEXT + ".* cuda-version=12.9", + cu13: "conda install -c rapidsai-nightly -c conda-forge -c nvidia libcuopt=" + V_CONDA_NEXT + ".* cuda-version=13.0", + }, + }, + container: containerLib, + }, + server: { + pip: { + stable: { + cu12: "pip install --extra-index-url=https://pypi.nvidia.com 'nvidia-cuda-runtime-cu12==12.9.*' 'cuopt-server-cu12==" + V + ".*' 'cuopt-sh-client==" + V_CONDA + ".*'", + cu13: "pip install --extra-index-url=https://pypi.nvidia.com 'nvidia-cuda-runtime==13.0.*' 'cuopt-server-cu13==" + V + ".*' 'cuopt-sh-client==" + V_CONDA + ".*'", + }, + nightly: { + cu12: "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'cuopt-server-cu12==" + V_NEXT + ".*' 'cuopt-sh-client==" + V_CONDA_NEXT + ".*'", + cu13: "pip install --pre --extra-index-url=https://pypi.nvidia.com --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple/ 'cuopt-server-cu13==" + V_NEXT + ".*' 'cuopt-sh-client==" + V_CONDA_NEXT + ".*'", + }, + }, + conda: { + stable: { + default: "conda install -c rapidsai -c conda-forge -c nvidia cuopt-server=" + V_CONDA + ".* cuopt-sh-client=" + V_CONDA + ".*", + }, + nightly: { + default: "conda install -c rapidsai-nightly -c conda-forge -c nvidia cuopt-server=" + V_CONDA_NEXT + ".* cuopt-sh-client=" + V_CONDA_NEXT + ".*", + }, + }, + container: { + stable: { + cu12: { + default: "docker pull nvidia/cuopt:latest-cu12", + run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:latest-cu12", + }, + cu13: { + default: "docker pull nvidia/cuopt:latest-cu13", + run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:latest-cu13", + }, + "cu13-ubi10": { + default: "docker pull nvidia/cuopt:latest-cu13-ubi10", + run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:latest-cu13-ubi10", + }, + }, + nightly: { + cu12: { + default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu12", + run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:" + V_NEXT + ".0a-cu12", + }, + cu13: { + default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu13", + run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:" + V_NEXT + ".0a-cu13", + }, + "cu13-ubi10": { + default: "docker pull nvidia/cuopt:" + V_NEXT + ".0a-cu13-ubi10", + run: "docker run --gpus all -it --rm -p 8000:8000 -e CUOPT_SERVER_PORT=8000 nvidia/cuopt:" + V_NEXT + ".0a-cu13-ubi10", + }, + }, + }, + }, + }; + } + + var SUPPORTED_METHODS = { + python: ["pip", "conda", "container"], + c: ["pip", "conda", "container"], + server: ["pip", "conda", "container"], + cli: ["pip", "conda", "container"], + }; + + function getSelectedValue(name) { + var el = document.querySelector('input[name="' + name + '"]:checked'); + return el ? el.value : ""; + } + + function hasCudaVariants(COMMANDS, iface, method) { + var d = COMMANDS[iface] && COMMANDS[iface][method]; + if (!d || !d.stable) return false; + return !!(d.stable.cu12 && d.stable.cu13); + } + + function getCommand(COMMANDS) { + var iface = getSelectedValue("cuopt-iface"); + var method = getSelectedValue("cuopt-method"); + var release = getSelectedValue("cuopt-release"); + var cuda = getSelectedValue("cuopt-cuda"); + + /* CLI uses libcuopt (c) install; cuopt_cli is shipped with libcuopt. */ + if (iface === "cli") { + iface = "c"; + release = "stable"; + cuda = "cu12"; + } + + var data = COMMANDS[iface] && COMMANDS[iface][method]; + if (!data || !data[release]) return ""; + + var cmd = ""; + if (method === "container") { + var variant = getSelectedValue("cuopt-variant") || "ubuntu"; + var baseCuda = cuda || "cu12"; + var cudaKey = baseCuda + (variant === "ubi10" ? "-ubi10" : ""); + var fallbackKey = (variant === "ubi10") ? "cu13-ubi10" : "cu12"; + var c = data[release][cudaKey] || data[release][fallbackKey]; + var hubPull = c.default; + var tag = "latest-cu12"; + var tm = hubPull.match(/docker pull nvidia\/cuopt:(\S+)/); + if (tm) tag = tm[1]; + var registry = getSelectedValue("cuopt-registry") || "hub"; + var runLine = c.run; + if (registry === "ngc" && release === "nightly") { + cmd = + "# Nightly cuOpt container images are not published to NVIDIA NGC; use Docker Hub for nightly builds.\n" + + "# (Select \"Docker Hub\" above for the same commands without this note.)\n\n" + + "# Docker Hub (docker.io) — no registry login required for public pulls\n" + + hubPull + "\n\n" + + "# Run the container:\n" + runLine; + } else if (registry === "ngc") { + runLine = runLine.replace(/nvidia\/cuopt:/g, "nvcr.io/nvidia/cuopt/cuopt:"); + cmd = + "# NVIDIA NGC (nvcr.io) — authenticate once per session, then pull:\n" + + "docker login nvcr.io\n" + + "# Username: $oauthtoken\n" + + "# Password: \n\n" + + "docker pull nvcr.io/nvidia/cuopt/cuopt:" + tag + "\n\n" + + "# Run the container:\n" + runLine; + } else { + cmd = + "# Docker Hub (docker.io) — no registry login required for public pulls\n" + + hubPull + "\n\n" + + "# Run the container:\n" + runLine; + } + } else { + var key = data[release].cu12 && data[release].cu13 ? cuda : "default"; + cmd = data[release][key] || data[release].cu12 || data[release].cu13 || data[release].default || ""; + } + return cmd; + } + + function updateOutput(COMMANDS) { + var out = document.getElementById("cuopt-cmd-out"); + var copyBtn = document.getElementById("cuopt-copy-btn"); + var cmd = getCommand(COMMANDS); + out.value = cmd; + out.style.display = cmd ? "block" : "none"; + copyBtn.style.display = cmd ? "inline-flex" : "none"; + } + + function updateVisibility(COMMANDS) { + var method = getSelectedValue("cuopt-method"); + var iface = getSelectedValue("cuopt-iface"); + var allowed = SUPPORTED_METHODS[iface] || []; + var methodInputs = document.querySelectorAll('input[name="cuopt-method"]'); + methodInputs.forEach(function (input) { + var enabled = allowed.indexOf(input.value) !== -1; + input.disabled = !enabled; + var label = input.closest("label"); + if (label) label.style.display = enabled ? "" : "none"; + }); + if (allowed.indexOf(method) === -1 && allowed.length) { + var fallback = document.querySelector('input[name="cuopt-method"][value="' + allowed[0] + '"]'); + if (fallback) { + fallback.checked = true; + method = allowed[0]; + } + } + var cudaRow = document.getElementById("cuopt-cuda-row"); + var releaseRow = document.getElementById("cuopt-release-row"); + var releaseVisible = iface !== "cli"; + var ifaceForVariants = iface === "cli" ? "c" : iface; + var showCuda = + releaseVisible && + (method === "pip" || method === "conda" || method === "container") && + hasCudaVariants(COMMANDS, ifaceForVariants, method); + cudaRow.style.display = showCuda ? "table-row" : "none"; + releaseRow.style.display = releaseVisible ? "table-row" : "none"; + var variantRow = document.getElementById("cuopt-variant-row"); + if (variantRow) variantRow.style.display = method === "container" ? "table-row" : "none"; + var registryRow = document.getElementById("cuopt-registry-row"); + if (registryRow) registryRow.style.display = method === "container" ? "table-row" : "none"; + updateOutput(COMMANDS); + } + + function copyToClipboard() { + var out = document.getElementById("cuopt-cmd-out"); + if (!out.value) return; + out.select(); + out.setSelectionRange(0, 99999); + try { + document.execCommand("copy"); + var btn = document.getElementById("cuopt-copy-btn"); + var orig = btn.textContent; + btn.textContent = "Copied!"; + setTimeout(function () { btn.textContent = orig; }, 1500); + } catch (e) {} + } + + function render() { + if (!resolveVersion()) return false; + var root = document.getElementById("cuopt-install-selector"); + if (!root) return false; + + var COMMANDS = buildCommands(); + + root.innerHTML = + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + "
Interface' + + '' + + '' + + '' + + '' + + '
Method' + + '' + + '' + + '' + + '
Release' + + '' + + '' + + '
CUDA' + + '' + + '' + + '
" + + '
' + + '' + + '
' + + "
"; + + ["cuopt-iface", "cuopt-method", "cuopt-release", "cuopt-cuda", "cuopt-variant", "cuopt-registry"].forEach( + function (name) { + var inputs = document.querySelectorAll('input[name="' + name + '"]'); + inputs.forEach(function (input) { + input.addEventListener("change", function () { updateVisibility(COMMANDS); }); + }); + } + ); + document.getElementById("cuopt-copy-btn").addEventListener("click", copyToClipboard); + updateVisibility(COMMANDS); + + var defaultIface = root.getAttribute("data-default-iface"); + if (defaultIface && ["python", "c", "server", "cli"].indexOf(defaultIface) !== -1) { + var radio = document.querySelector('input[name="cuopt-iface"][value="' + defaultIface + '"]'); + if (radio) { radio.checked = true; updateVisibility(COMMANDS); } + } + return true; + } + + function isRendered() { + var root = document.getElementById("cuopt-install-selector"); + return root ? !!root.querySelector(".cuopt-install-selector-wrap") : false; + } + + /* Polling loop — runs every 150 ms for up to ~10 s after each navigation. + Handles: version script loading after us, React wiping content mid-reconcile, + RSC streaming pages where the div appears after pushState fires. */ + var _pollTimer = null; + var _pollGen = 0; + + function poll(gen, remaining) { + if (gen !== _pollGen) return; + if (!isRendered()) render(); + if (isRendered() && remaining > 20) { + /* Widget is up; keep a cheap watch for a bit in case React wipes it again. */ + remaining = 20; + } + if (remaining > 0) { + _pollTimer = setTimeout(function () { poll(gen, remaining - 1); }, 150); + } + } + + function startPoll() { + _pollGen++; + if (_pollTimer) { clearTimeout(_pollTimer); _pollTimer = null; } + poll(_pollGen, 66); /* ~10 s */ + } + + /* Hook Next.js SPA navigation (pushState / replaceState / back-forward). */ + var _origPush = history.pushState.bind(history); + history.pushState = function () { _origPush.apply(history, arguments); startPoll(); }; + var _origReplace = history.replaceState.bind(history); + history.replaceState = function () { _origReplace.apply(history, arguments); startPoll(); }; + window.addEventListener("popstate", startPoll); + + startPoll(); +})(); diff --git a/fern/embed_examples.py b/fern/embed_examples.py new file mode 100644 index 0000000000..c502165788 --- /dev/null +++ b/fern/embed_examples.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Embed example source files into *-examples.mdx pages as fenced code blocks. + +How it works +------------ +MDX example pages contain either: + + (a) a bare link stub on its own line: + [filename.py](examples/filename.py) + + (b) a previously-generated embed block: + + ```python + ...code... + ``` + + +On each run this script: + - Converts case (a) to case (b). + - Refreshes the code inside any existing case (b) marker pair. + +The example source files live alongside the MDX pages in each + fern/docs/pages//
/examples/ +directory. + +Usage: + python fern/embed_examples.py +""" + +import re +from pathlib import Path + +REPO_ROOT = Path(__file__).parent.parent +PAGES = REPO_ROOT / "fern/docs/pages" + +# MDX files to process, each with the examples directory next to it +EXAMPLE_PAGES = [ + PAGES / "cuopt-python/convex/convex-examples.mdx", + PAGES / "cuopt-python/mip/mip-examples.mdx", + PAGES / "cuopt-python/routing/routing-examples.mdx", + PAGES / "cuopt-c/convex/convex-examples.mdx", + PAGES / "cuopt-c/mip/mip-examples.mdx", +] + +_EXT_LANG = { + ".py": "python", + ".c": "c", + ".sh": "bash", + ".mps": "text", + ".lp": "text", +} + +# Matches a standalone link to an example file: +# [any\_text\_with\_escapes](examples/filename.ext) +_LINK_RE = re.compile(r"^\[([^\]]+)\]\(examples/([^)]+)\)\s*$") + +# Matches the opening embed marker: {/* embed: examples/filename.ext */} +# Also accepts legacy HTML comment form for migration: +_OPEN_RE = re.compile( + r"^(?:\{/\*\s*embed:\s*examples/(\S+?)\s*\*/\}|)$" +) +_CLOSE_NEW = "{/* /embed */}" +_CLOSE_LEGACY = "" + + +def _code_block(src_file: Path, rel: str) -> str: + lang = _EXT_LANG.get(Path(rel).suffix, "text") + code = src_file.read_text(encoding="utf-8").rstrip() + return f"{{/* embed: examples/{rel} */}}\n```{lang}\n{code}\n```\n{_CLOSE_NEW}" + + +def _process(mdx_path: Path) -> bool: + """Embed or refresh code blocks in one MDX file. Returns True if changed.""" + examples_dir = mdx_path.parent / "examples" + original = mdx_path.read_text(encoding="utf-8") + lines = original.splitlines(keepends=True) + out = [] + i = 0 + while i < len(lines): + line = lines[i].rstrip("\n") + + # Case (a): bare link stub → embed + m = _LINK_RE.match(line) + if m: + rel = m.group(2) # e.g. "simple_lp_example.py" + src = examples_dir / rel + if src.exists(): + block = _code_block(src, rel) + out.append(block + "\n") + i += 1 + continue + # No source file found — leave as-is + out.append(lines[i]) + i += 1 + continue + + # Case (b): existing embed marker → refresh content + mo = _OPEN_RE.match(line) + if mo: + rel = mo.group(1) or mo.group(2) + src = examples_dir / rel + # Skip everything up to and including the closing marker + i += 1 + while i < len(lines) and lines[i].rstrip("\n") not in ( + _CLOSE_NEW, + _CLOSE_LEGACY, + ): + i += 1 + i += 1 # skip the closing marker line + if src.exists(): + block = _code_block(src, rel) + out.append(block + "\n") + else: + # Source gone — emit a warning comment and leave a placeholder + out.append(f"{{/* embed: examples/{rel} */}}\n") + out.append(f"*Example `{rel}` not found.*\n") + out.append(f"{_CLOSE_NEW}\n") + continue + + out.append(lines[i]) + i += 1 + + result = "".join(out) + if result != original: + mdx_path.write_text(result, encoding="utf-8") + return True + return False + + +def embed_all(): + for mdx in EXAMPLE_PAGES: + if not mdx.exists(): + print(f" [SKIP] {mdx.relative_to(REPO_ROOT)} (not found)") + continue + changed = _process(mdx) + status = "updated" if changed else "unchanged" + print(f" {status}: {mdx.relative_to(REPO_ROOT)}") + + +if __name__ == "__main__": + print("Embedding examples into MDX pages...") + embed_all() + print("Done.") diff --git a/fern/extract_c_api.py b/fern/extract_c_api.py new file mode 100644 index 0000000000..6a7e92cd2a --- /dev/null +++ b/fern/extract_c_api.py @@ -0,0 +1,579 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Parse cuopt_c.h and constants.h via Doxygen XML and refresh the C API MDX +skeleton pages. + +How it works +------------ +Each MDX skeleton page (checked into git) contains placeholder markers: + + + + +On each run this script: + 1. Runs Doxygen to extract symbols from the C headers. + 2. For each skeleton file, finds every marker and + replaces the content between it and with freshly + rendered Doxygen documentation. + 3. Errors if any symbol found in Doxygen XML has no marker in any skeleton. + This forces developers to consciously place new symbols when they add them + to the headers. + +To add a new symbol to the docs: + 1. Add a marker in the right + location in the appropriate skeleton MDX file. + 2. Re-run the script (or build docs) — the content fills in automatically. + +Usage: + python fern/extract_c_api.py + +Regenerates: + fern/docs/pages/cuopt-c/convex/convex-c-api.mdx + fern/docs/pages/cuopt-c/mip/mip-c-api.mdx +""" + +import re +import subprocess +import sys +import xml.etree.ElementTree as ET +from pathlib import Path + +REPO_ROOT = Path(__file__).parent.parent +HEADER = REPO_ROOT / "cpp/include/cuopt/mathematical_optimization/cuopt_c.h" +CONSTANTS = ( + REPO_ROOT / "cpp/include/cuopt/mathematical_optimization/constants.h" +) +PAGES = REPO_ROOT / "fern/docs/pages" + +# MDX skeleton files — checked into git; script refreshes symbol blocks in-place. +SKELETON_PAGES = [ + PAGES / "cuopt-c/convex/convex-c-api.mdx", + PAGES / "cuopt-c/mip/mip-c-api.mdx", +] + +_DOXYFILE = REPO_ROOT / "fern/Doxyfile" +_XML_DIR = REPO_ROOT / "fern/.doxygen-xml/xml" + +# Symbols to always suppress (internal guards, macros not for users) +_INTERNAL_SYMBOLS = { + "CUOPT_C_API_H", + "CUOPT_CONSTANTS_H", +} +_INTERNAL_PREFIXES = ("CUOPT_INSTANTIATE_",) + +# Marker regexes — JSX comment form is canonical; legacy HTML comment form accepted for migration +_OPEN_RE = re.compile( + r"^(?:\{/\*\s*symbol:\s*(\S+?)\s*\*/\}|)$" +) +_CLOSE_NEW = "{/* /symbol */}" +_CLOSE_LEGACY = "" + + +# --------------------------------------------------------------------------- +# Doxygen XML parser +# --------------------------------------------------------------------------- + + +def _xml_text(elem) -> str: + if elem is None: + return "" + parts = [] + if elem.text: + parts.append(elem.text) + for child in elem: + if child.tag == "computeroutput": + parts.append(f"`{_xml_text(child)}`") + else: + parts.append(_xml_text(child)) + if child.tail: + parts.append(child.tail) + return "".join(parts).strip() + + +def _parse_memberdef(member) -> dict | None: + kind = member.get("kind", "") + name_el = member.find("name") + if name_el is None: + return None + name = name_el.text or "" + + brief_el = member.find("briefdescription") + brief = "" + if brief_el is not None: + para = brief_el.find("para") + if para is not None: + brief = _xml_text(para) + + detail_el = member.find("detaileddescription") + param_docs = [] + return_doc = "" + note = "" + deprecated = "" + + if detail_el is not None: + for para in detail_el.iter("para"): + for plist in para.findall("parameterlist"): + if plist.get("kind") == "param": + for item in plist.findall("parameteritem"): + pname_list = item.find("parameternamelist") + pdesc_el = item.find("parameterdescription") + if pname_list is None: + continue + pname_el = pname_list.find("parametername") + pname = ( + pname_el.text or "" if pname_el is not None else "" + ) + direction = ( + pname_el.get("direction", "") + if pname_el is not None + else "" + ) + pdesc = "" + if pdesc_el is not None: + inner = pdesc_el.find("para") + pdesc = ( + _xml_text(inner) + if inner is not None + else _xml_text(pdesc_el) + ) + pdesc = re.sub(r"^-\s+", "", pdesc.strip()) + param_docs.append( + {"name": pname, "dir": direction, "desc": pdesc} + ) + + for ss in para.findall("simplesect"): + if ss.get("kind") == "return": + inner = ss.find("para") + return_doc = ( + _xml_text(inner) + if inner is not None + else _xml_text(ss) + ) + elif ss.get("kind") in ("note", "attention"): + inner = ss.find("para") + note_text = ( + _xml_text(inner) + if inner is not None + else _xml_text(ss) + ) + note = ( + (note + " " + note_text).strip() if note else note_text + ) + elif ss.get("kind") == "warning": + inner = ss.find("para") + dep_text = ( + _xml_text(inner) + if inner is not None + else _xml_text(ss) + ) + deprecated = ( + (deprecated + " " + dep_text).strip() + if deprecated + else dep_text + ) + + if kind == "define": + init_el = member.find("initializer") + value = (init_el.text or "").strip() if init_el is not None else "" + return {"kind": "define", "value": value, "brief": brief, "name": name} + + elif kind == "typedef": + type_el = member.find("type") + args_el = member.find("argsstring") + underlying = _xml_text(type_el) if type_el is not None else "" + args = args_el.text or "" if args_el is not None else "" + if args.strip().startswith(")(") or args.strip().startswith("*)("): + return { + "kind": "typedef_fn", + "name": name, + "brief": brief, + "param_docs": param_docs, + "return_doc": return_doc, + "note": note, + } + underlying = underlying.replace("`", "").strip() + return { + "kind": "typedef", + "underlying": underlying, + "brief": brief, + "name": name, + } + + elif kind == "function": + type_el = member.find("type") + ret = _xml_text(type_el) if type_el is not None else "" + ret = ret.replace("`", "").strip() + decl_params = [] + for param in member.findall("param"): + ptype_el = param.find("type") + pname_el = param.find("declname") + ptype = _xml_text(ptype_el) if ptype_el is not None else "" + ptype = ptype.replace("`", "").strip() + pname = pname_el.text or "" if pname_el is not None else "" + decl_params.append({"type": ptype, "name": pname}) + return { + "kind": "function", + "name": name, + "ret": ret, + "decl_params": decl_params, + "brief": brief, + "param_docs": param_docs, + "return_doc": return_doc, + "note": note, + "deprecated": deprecated, + } + + return None + + +def parse_headers() -> dict: + """Run Doxygen and return all parsed symbols keyed by name.""" + if not _DOXYFILE.exists(): + raise FileNotFoundError(f"Doxyfile not found: {_DOXYFILE}") + + result = subprocess.run( + ["doxygen", str(_DOXYFILE)], + cwd=str(REPO_ROOT), + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise RuntimeError( + f"doxygen failed (exit {result.returncode}):\n{result.stderr}" + ) + + if not _XML_DIR.exists(): + raise FileNotFoundError( + f"Doxygen XML output dir not found: {_XML_DIR}" + ) + + symbols: dict = {} + for xml_file in sorted(_XML_DIR.glob("*.xml")): + if xml_file.name in ("index.xml", "combine.xslt"): + continue + if not ( + xml_file.name.startswith("constants_") + or xml_file.name.startswith("cuopt__c_") + ): + continue + try: + tree = ET.parse(xml_file) + except ET.ParseError as e: + print(f" [WARN] XML parse error in {xml_file.name}: {e}") + continue + root = tree.getroot() + for compound in root.findall(".//compounddef"): + for section in compound.findall("sectiondef"): + for member in section.findall("memberdef"): + info = _parse_memberdef(member) + if info is None: + continue + name = info.pop("name") + if name in _INTERNAL_SYMBOLS: + continue + if any(name.startswith(p) for p in _INTERNAL_PREFIXES): + continue + symbols[name] = info + + _supplement_if_guarded_typedefs(symbols) + return symbols + + +def _supplement_if_guarded_typedefs(symbols: dict): + """Regex fallback for typedefs inside #if blocks that Doxygen skips.""" + text = HEADER.read_text(encoding="utf-8") + lines = text.splitlines() + n = len(lines) + + consts_text = CONSTANTS.read_text(encoding="utf-8") + macro_values: dict[str, int] = {} + for m in re.finditer( + r"^#define\s+(CUOPT_INSTANTIATE_\w+)\s+(\d+)", consts_text, re.M + ): + macro_values[m.group(1)] = int(m.group(2)) + + in_active_block = False + pending_comment: list[str] = [] + i = 0 + while i < n: + line = lines[i] + m_if = re.match(r"^#if\s+(CUOPT_INSTANTIATE_\w+)", line) + m_endif = re.match(r"^#endif", line) + + if m_if: + macro = m_if.group(1) + in_active_block = bool(macro_values.get(macro, 0)) + pending_comment = [] + i += 1 + continue + + if m_endif: + in_active_block = False + pending_comment = [] + i += 1 + continue + + if in_active_block: + if line.strip().startswith("/**") or line.strip().startswith("*"): + pending_comment.append(line) + elif re.match(r"^typedef\s+(.*?)\s+(\w+)\s*;", line): + m_td = re.match(r"^typedef\s+(.*?)\s+(\w+)\s*;", line) + underlying = m_td.group(1).strip() + name = m_td.group(2) + if name not in symbols: + brief = "" + if pending_comment: + raw = "\n".join(pending_comment) + cleaned = re.sub(r"/\*+", "", raw) + cleaned = re.sub(r"\*/", "", cleaned) + cleaned = re.sub(r"^\s*\*\s?", "", cleaned, flags=re.M) + brief_m = re.search(r"@brief\s+(.*)", cleaned, re.S) + if brief_m: + brief = re.sub( + r"\s+", " ", brief_m.group(1) + ).strip() + else: + brief = re.sub(r"\s+", " ", cleaned).strip() + symbols[name] = { + "kind": "typedef", + "underlying": underlying, + "brief": brief, + } + pending_comment = [] + else: + if line.strip() and not line.strip().startswith("//"): + pending_comment = [] + + i += 1 + + +# --------------------------------------------------------------------------- +# MDX renderers +# --------------------------------------------------------------------------- + + +def _escape_mdx(text: str) -> str: + text = re.sub(r"<=", "<=", text) + text = text.replace("{", "(").replace("}", ")") + return text + + +def _clean_desc(text: str) -> str: + text = text.strip() + text = re.sub(r"^-\s+", "", text) + text = re.sub(r"``([^`]+)``", r"`\1`", text) + return text + + +def _render_typedef(name: str, info: dict) -> str: + brief = _escape_mdx(_clean_desc(info.get("brief", "") or "")) + underlying = info.get("underlying", "") + if underlying: + return f"**`typedef`** **`{name}`** — {brief} (`typedef {underlying}`)" + return f"**`typedef`** **`{name}`** — {brief}" + + +def _render_typedef_fn(name: str, info: dict) -> str: + lines = [] + brief = _escape_mdx(_clean_desc(info.get("brief", "") or "")) + lines.append(f"**`typedef`** **`{name}`** — {brief}") + note = info.get("note", "") + if note: + lines.append(f"\n\n{_escape_mdx(_clean_desc(note))}\n") + param_docs = info.get("param_docs", []) + if param_docs: + lines.append("\n**Parameters**\n") + for p in param_docs: + desc = _escape_mdx(_clean_desc(p.get("desc", "") or "")) + direction = p.get("dir", "") + dir_str = f" `[{direction}]`" if direction else "" + lines.append(f"- **`{p['name']}`**{dir_str} — {desc}") + ret = info.get("return_doc", "") + if ret: + lines.append(f"\n**Returns** {_escape_mdx(_clean_desc(ret))}") + return "\n".join(lines) + + +def _render_define(name: str, info: dict) -> str: + value = info.get("value", "") + brief = _escape_mdx(_clean_desc(info.get("brief", "") or "")) + if brief: + return f"- `{name}` (`{value}`) — {brief}" + return f"- `{name}` (`{value}`)" + + +def _render_function(name: str, info: dict) -> str: + lines = [] + ret = info.get("ret", "") + decl_params = info.get("decl_params", []) + param_str = ( + ", ".join(f"{p['type']} {p['name']}".strip() for p in decl_params) + or "void" + ) + sig = f"{name}({param_str})" + if ret and ret != "void": + sig = f"{sig} -> {ret}" + lines.append(f"#### `{sig}`\n") + + brief = _escape_mdx(_clean_desc(info.get("brief", "") or "")) + if brief: + lines.append(f"{brief}\n") + + deprecated = info.get("deprecated", "") + if deprecated: + lines.append( + f"\n{_escape_mdx(_clean_desc(deprecated))}\n\n" + ) + + note = info.get("note", "") + if note: + lines.append(f"\n{_escape_mdx(_clean_desc(note))}\n\n") + + param_docs = info.get("param_docs", []) + if param_docs: + lines.append("**Parameters**\n") + for p in param_docs: + pname = p.get("name", "") + direction = p.get("dir", "") + desc = _escape_mdx(_clean_desc(p.get("desc", "") or "")) + dir_str = f" `[{direction}]`" if direction else "" + ptype = next( + (dp["type"] for dp in decl_params if dp["name"] == pname), "" + ) + if ptype: + lines.append(f"- **`{pname}`** (`{ptype}`){dir_str} — {desc}") + else: + lines.append(f"- **`{pname}`**{dir_str} — {desc}") + lines.append("") + + ret_doc = info.get("return_doc", "") + if ret_doc: + lines.append(f"**Returns** {_escape_mdx(_clean_desc(ret_doc))}\n") + + return "\n".join(lines) + + +def _render_symbol(name: str, symbols: dict) -> str: + info = symbols.get(name) + if not info: + return f"*`{name}` — documentation not found in headers.*" + kind = info.get("kind", "") + if kind == "function": + return _render_function(name, info) + if kind == "typedef_fn": + return _render_typedef_fn(name, info) + if kind == "typedef": + return _render_typedef(name, info) + if kind == "define": + return _render_define(name, info) + return f"*`{name}` — (see header)*" + + +# --------------------------------------------------------------------------- +# Skeleton refresher +# --------------------------------------------------------------------------- + + +def _collect_markers(mdx_path: Path) -> set: + """Return the set of symbol names with markers in this skeleton file.""" + found = set() + for line in mdx_path.read_text(encoding="utf-8").splitlines(): + m = _OPEN_RE.match(line.strip()) + if m: + found.add(m.group(1) or m.group(2)) + return found + + +def _refresh_skeleton(mdx_path: Path, symbols: dict) -> bool: + """ + Replace content between {/* symbol: NAME */} and {/* /symbol */} markers + with freshly rendered Doxygen output. Returns True if the file changed. + """ + original = mdx_path.read_text(encoding="utf-8") + lines = original.splitlines(keepends=True) + out = [] + i = 0 + while i < len(lines): + line = lines[i].rstrip("\n") + m = _OPEN_RE.match(line.strip()) + if m: + name = m.group(1) or m.group(2) + # Always emit opening marker in canonical JSX form + out.append(f"{{/* symbol: {name} */}}\n") + i += 1 + # skip existing content up to closing marker (either form) + while i < len(lines) and lines[i].rstrip("\n").strip() not in ( + _CLOSE_NEW, + _CLOSE_LEGACY, + ): + i += 1 + # render fresh content + rendered = _render_symbol(name, symbols).rstrip() + if rendered: + out.append(rendered + "\n") + # emit closing marker in canonical JSX form + out.append(_CLOSE_NEW + "\n") + i += 1 # skip the closing marker line we consumed + continue + out.append(lines[i]) + i += 1 + + result = "".join(out) + if result != original: + mdx_path.write_text(result, encoding="utf-8") + return True + return False + + +# --------------------------------------------------------------------------- +# Main entry point +# --------------------------------------------------------------------------- + + +def generate_pages(): + print("Extracting C API docs from headers (Doxygen XML)...") + symbols = parse_headers() + print(f" Parsed {len(symbols)} symbols") + + # Collect every symbol name referenced in any skeleton + all_placed: set = set() + for skeleton in SKELETON_PAGES: + if skeleton.exists(): + all_placed |= _collect_markers(skeleton) + + # Error on any Doxygen symbol that has no marker in any skeleton + missing = sorted( + name + for name in symbols + if name not in all_placed + and name not in _INTERNAL_SYMBOLS + and not any(name.startswith(p) for p in _INTERNAL_PREFIXES) + ) + if missing: + print( + f"\nERROR: {len(missing)} symbol(s) found in headers but not placed in any MDX skeleton.\n" + "Add a {/* symbol: NAME */}{/* /symbol */} marker to the appropriate skeleton file:\n" + + "\n".join(f" - {n}" for n in missing), + file=sys.stderr, + ) + sys.exit(1) + + # Refresh each skeleton + for skeleton in SKELETON_PAGES: + if not skeleton.exists(): + print(f" [SKIP] {skeleton.relative_to(REPO_ROOT)} (not found)") + continue + changed = _refresh_skeleton(skeleton, symbols) + status = "updated" if changed else "unchanged" + print(f" {status}: {skeleton.relative_to(REPO_ROOT)}") + + +# Alias expected by generate_api_docs.py +main = generate_pages + + +if __name__ == "__main__": + generate_pages() diff --git a/fern/extract_python_api.py b/fern/extract_python_api.py new file mode 100644 index 0000000000..e2ba95d4a3 --- /dev/null +++ b/fern/extract_python_api.py @@ -0,0 +1,838 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Extract Python API docstrings via AST (no imports, no GPU) and write MDX pages. + +Usage: + python fern/extract_python_api.py + +Generates MDX for the Python API reference pages. Pure-Python files are parsed +with ast.parse(); Cython .pyx files are parsed with a regex line-scanner that +handles 'class Foo(Base):' while skipping 'cdef class' extension types. +""" + +import ast +import inspect +import re +from pathlib import Path + +try: + from numpydoc.docscrape import NumpyDocString + + _NUMPYDOC_AVAILABLE = True +except ImportError: + _NUMPYDOC_AVAILABLE = False + +REPO_ROOT = Path(__file__).parent.parent +PYTHON_SRC = REPO_ROOT / "python/cuopt/cuopt" +PAGES = REPO_ROOT / "fern/docs/pages" +DOCS_V26_08 = REPO_ROOT / "fern/docs-v26-08.yml" + +# --------------------------------------------------------------------------- +# Page configuration — maps output MDX path to source directories. +# Each directory is scanned non-recursively (*.py + *.pyx, no subdirs). +# --------------------------------------------------------------------------- +PAGE_SOURCES = [ + { + "output": "cuopt-python/convex/convex-api.mdx", + "title": "Convex Optimization Python API Reference", + # RST order: Problem → Variable → LinearExpression → QuadraticExpression → + # Constraint → SolverSettings → CType → sense → Read + "dirs": [ + # problem.py: Problem, Variable, LinearExpression, QuadraticExpression, + # Constraint, VType, CType, sense — all in source order + {"path": "linear_programming", "use_init_all": False}, + # SolverSettings, SolverMethod, PDLPSolverMode + { + "path": "linear_programming/solver_settings", + "use_init_all": True, + }, + # Read, ParseMps + { + "path": "linear_programming/io", + "use_init_all": True, + "exclude": {"parser_wrapper"}, + }, + # DataModel (low-level; after main API) + { + "path": "linear_programming/data_model", + "use_init_all": True, + "exclude": {"data_model_wrapper"}, + }, + # Solution, PDLPWarmStartData + {"path": "linear_programming/solution", "use_init_all": True}, + # Solve, BatchSolve + { + "path": "linear_programming/solver", + "use_init_all": True, + "exclude": {"solver_wrapper"}, + }, + ], + }, + { + "output": "cuopt-python/mip/mip-api.mdx", + "title": "MIP Python API Reference", + "dirs": [ + { + "path": "linear_programming/data_model", + "use_init_all": True, + "exclude": {"data_model_wrapper"}, + }, + {"path": "linear_programming/solution", "use_init_all": True}, + { + "path": "linear_programming/solver", + "use_init_all": True, + "exclude": {"solver_wrapper"}, + }, + ], + }, + { + "output": "cuopt-python/routing/routing-api.mdx", + "title": "cuOpt Routing Python API Reference", + # RST order: WaypointMatrix → SolutionStatus → DataModel → SolverSettings + # → Solve → BatchSolve → Assignment + "dirs": [ + # WaypointMatrix comes first per existing RST + { + "path": "distance_engine", + "use_init_all": True, + "exclude": {"waypoint_matrix_wrapper"}, + }, + # routing/__init__.py __all__ filters internals; wrapper pyx get no heading + { + "path": "routing", + "use_init_all": True, + "exclude": {"validation", "waypoint_matrix_wrapper"}, + "order": [ + "vehicle_routing", # DataModel, SolverSettings, Solve, BatchSolve + "assignment", # SolutionStatus, Assignment + "utils", # generate_dataset + utility functions + "utils_wrapper", # DatasetDistribution (inline, no heading) + "vehicle_routing_wrapper", # ErrorStatus, NodeType, Objective (inline) + ], + }, + ], + }, +] + + +# --------------------------------------------------------------------------- +# AST helpers (.py files) +# --------------------------------------------------------------------------- + + +def _get_docstring(node) -> str: + if ( + node.body + and isinstance(node.body[0], ast.Expr) + and isinstance(node.body[0].value, ast.Constant) + and isinstance(node.body[0].value.value, str) + ): + return inspect.cleandoc(node.body[0].value.value) + return "" + + +def _format_sig(node) -> str: + args = node.args + parts = [] + n_defaults = len(args.defaults) + n_args = len(args.args) + for i, arg in enumerate(args.args): + default_idx = i - (n_args - n_defaults) + if arg.arg == "self": + continue + annot = "" + if arg.annotation: + try: + annot = ": " + ast.unparse(arg.annotation) + except Exception: + pass + if default_idx >= 0: + try: + default = " = " + ast.unparse(args.defaults[default_idx]) + except Exception: + default = " = ..." + parts.append(f"{arg.arg}{annot}{default}") + else: + parts.append(f"{arg.arg}{annot}") + if args.vararg: + parts.append(f"*{args.vararg.arg}") + if args.kwarg: + parts.append(f"**{args.kwarg.arg}") + ret = "" + if node.returns: + try: + ret = " -> " + ast.unparse(node.returns) + except Exception: + pass + return f"{node.name}({', '.join(parts)}){ret}" + + +def _parse_module(path: Path) -> dict: + """Parse a .py file via AST and return classes, functions, module_doc.""" + try: + tree = ast.parse(path.read_text(encoding="utf-8")) + except SyntaxError as e: + print(f" [WARN] syntax error in {path}: {e}") + return {"module_doc": "", "classes": [], "functions": []} + + module_doc = ast.get_docstring(tree) or "" + classes = [] + functions = [] + + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef) and node.col_offset == 0: + methods = [] + members = [] + # Detect IntEnum subclasses: collect their member names + bases = [ + ast.unparse(b) for b in node.bases if isinstance(b, ast.expr) + ] + is_enum = any("Enum" in b or "enum" in b.lower() for b in bases) + for item in node.body: + if is_enum and isinstance(item, ast.Assign): + for t in item.targets: + if isinstance(t, ast.Name) and not t.id.startswith( + "_" + ): + members.append(t.id) + elif isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)): + if item.name.startswith("_") and item.name != "__init__": + continue + sig = _format_sig(item) + doc = _get_docstring(item) + methods.append( + {"name": item.name, "doc": doc, "signature": sig} + ) + classes.append( + { + "name": node.name, + "doc": _get_docstring(node), + "methods": methods, + "members": members, + } + ) + + elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + if node.col_offset == 0 and not node.name.startswith("_"): + sig = _format_sig(node) + doc = _get_docstring(node) + functions.append( + {"name": node.name, "doc": doc, "signature": sig} + ) + + return { + "module_doc": module_doc, + "classes": classes, + "functions": functions, + } + + +# --------------------------------------------------------------------------- +# Regex helpers (.pyx files) +# --------------------------------------------------------------------------- + + +def _collect_pyx_docstring(lines: list[str], start: int) -> str: + """Collect a docstring starting at `start`, skipping blank lines.""" + i = start + while i < len(lines) and not lines[i].strip(): + i += 1 + if i >= len(lines): + return "" + stripped = lines[i].strip() + for q in ('"""', "'''"): + if stripped.startswith(q): + # Check for single-line docstring + rest = stripped[len(q) :] + if q in rest: + return inspect.cleandoc(rest[: rest.index(q)]) + # Multi-line + doc_lines = [rest] + i += 1 + while i < len(lines): + s = lines[i] + if q in s: + doc_lines.append(s[: s.index(q)]) + break + doc_lines.append(s) + i += 1 + return inspect.cleandoc("\n".join(doc_lines)) + return "" + + +def _parse_module_pyx(path: Path) -> dict: + """ + Parse a Cython .pyx file for user-facing symbols: + - Regular Python classes (class Foo(Base):) — enums, thin wrappers + - Top-level def functions (not cpdef/cdef; those are C-layer internals) + Skips 'cdef class' extension types (not importable as plain Python objects). + """ + try: + text = path.read_text(encoding="utf-8") + except OSError: + return {"module_doc": "", "classes": [], "functions": []} + + lines = text.splitlines() + classes = [] + functions = [] + + i = 0 + while i < len(lines): + raw = lines[i] + stripped = raw.strip() + indent = len(raw) - len(raw.lstrip()) + + if indent != 0: + i += 1 + continue + + # Skip cdef class / cpdef / cdef (C-layer definitions) + if re.match(r"(cdef|cpdef)\s", stripped): + i += 1 + continue + + # Regular Python class at module level + m = re.match(r"class\s+([A-Za-z_]\w*)\s*(?:\([^)]*\))?\s*:", stripped) + if m: + name = m.group(1) + if name.startswith("_"): + i += 1 + continue + # Base classes — detect enums + base_match = re.match(r"class\s+\w+\s*\(([^)]*)\)", stripped) + bases = base_match.group(1) if base_match else "" + is_enum = "Enum" in bases or "enum" in bases.lower() + + doc = _collect_pyx_docstring(lines, i + 1) + + # Collect body: enum members and def methods + members = [] + methods = [] + j = i + 1 + while j < len(lines): + bline = lines[j] + bindent = len(bline) - len(bline.lstrip()) + bstripped = bline.strip() + + # End of class body: unindented non-blank line + if bstripped and bindent == 0: + break + + if bindent > 0 and bstripped: + if is_enum: + # Match both SCREAMING_SNAKE_CASE and PascalCase enum members + em = re.match(r"([A-Za-z_]\w*)\s*=", bstripped) + if em and not em.group(1).startswith("_"): + members.append(em.group(1)) + # def methods (skip cdef/cpdef) + dm = re.match(r"def\s+([A-Za-z_]\w*)\s*\(", bstripped) + if dm: + mname = dm.group(1) + if not mname.startswith("_") or mname == "__init__": + mdoc = _collect_pyx_docstring(lines, j + 1) + # Collect the signature (single line for simplicity) + sig_m = re.match( + r"def\s+(\w+\s*\([^)]*\))", bstripped + ) + sig = sig_m.group(1) if sig_m else f"{mname}(...)" + methods.append( + {"name": mname, "doc": mdoc, "signature": sig} + ) + j += 1 + + classes.append( + { + "name": name, + "doc": doc, + "methods": methods, + "members": members, + } + ) + i = j + continue + + # Top-level def (not cpdef/cdef — already skipped above) + fm = re.match(r"def\s+([A-Za-z_]\w*)\s*\(", stripped) + if fm: + fname = fm.group(1) + if not fname.startswith("_"): + doc = _collect_pyx_docstring(lines, i + 1) + # Collect full signature (may span multiple lines until ":") + sig_lines = [stripped.rstrip(":")] + j = i + 1 + while j < len(lines) and ":" not in sig_lines[-1]: + sig_lines.append(lines[j].strip()) + j += 1 + sig = " ".join(sig_lines).rstrip(":") + functions.append({"name": fname, "doc": doc, "signature": sig}) + + i += 1 + + return {"module_doc": "", "classes": classes, "functions": functions} + + +# --------------------------------------------------------------------------- +# MDX renderers +# --------------------------------------------------------------------------- + +_EXAMPLE_SECTIONS = {"Examples", "Example"} +_LIST_SECTIONS = { + "Parameters", + "Returns", + "Raises", + "Attributes", + "Other Parameters", + "Yields", +} + + +def _doc_to_mdx(doc: str) -> str: + """Minimal docstring → MDX: preserve code blocks, wrap numpy-style sections.""" + if not doc: + return "" + lines = doc.splitlines() + out = [] + i = 0 + while i < len(lines): + line = lines[i] + if ( + i + 1 < len(lines) + and lines[i + 1].strip() + and all(c == "-" for c in lines[i + 1].strip()) + ): + section = line.strip() + out.append(f"\n**{section}**\n") + i += 2 + body_lines = [] + while i < len(lines): + pline = lines[i] + if ( + i + 1 < len(lines) + and pline.strip() + and not pline[0].isspace() + and lines[i + 1].strip() + and all(c == "-" for c in lines[i + 1].strip()) + ): + break + body_lines.append(pline) + i += 1 + if section in _EXAMPLE_SECTIONS: + code = "\n".join(body_lines).strip() + if code: + out.append("```python") + out.append(code) + out.append("```") + out.append("") + elif section in _LIST_SECTIONS: + entries = [] + current_name = None + current_desc = [] + remaining = [] + in_list = True + for pline in body_lines: + if not in_list: + remaining.append(pline) + continue + if not pline.strip(): + continue + if pline[0].isspace(): + current_desc.append(pline.strip()) + else: + s = pline.strip() + if s.endswith(":") and " : " not in s: + if current_name is not None: + entries.append((current_name, current_desc)) + current_name = None + in_list = False + remaining.append(pline) + else: + if current_name is not None: + entries.append((current_name, current_desc)) + current_name = s + current_desc = [] + if current_name is not None: + entries.append((current_name, current_desc)) + for name, desc in entries: + desc_text = " ".join(desc).strip() + if desc_text: + out.append(f"- **`{name}`** — {desc_text}") + else: + out.append(f"- **`{name}`**") + if remaining: + out.append("") + out.extend(remaining) + out.append("") + else: + out.extend(body_lines) + out.append("") + continue + out.append(line) + i += 1 + return "\n".join(out) + + +def _doc_to_mdx_numpydoc(doc: str) -> str: + """Parse a NumPy-style docstring and render as MDX. Falls back to _doc_to_mdx.""" + if not doc: + return "" + if not _NUMPYDOC_AVAILABLE: + return _doc_to_mdx(doc) + try: + parsed = NumpyDocString(doc) + except Exception: + return _doc_to_mdx(doc) + + has_content = ( + parsed["Summary"] + or parsed["Extended Summary"] + or parsed["Parameters"] + or parsed["Returns"] + or parsed["Raises"] + or parsed["Examples"] + or parsed["Notes"] + or parsed["Attributes"] + ) + if not has_content: + return _doc_to_mdx(doc) + + out = [] + if parsed["Summary"]: + out.append(" ".join(parsed["Summary"])) + out.append("") + if parsed["Extended Summary"]: + out.append(" ".join(parsed["Extended Summary"])) + out.append("") + + def _render_params(label, items): + if not items: + return + out.append(f"**{label}**") + out.append("") + for p in items: + name = p.name or "" + ptype = p.type or "" + desc = " ".join(p.desc).strip() + header = ( + f"- **`{name}`** (`{ptype}`)" if ptype else f"- **`{name}`**" + ) + out.append(f"{header} — {desc}" if desc else header) + out.append("") + + _render_params("Parameters", parsed["Parameters"]) + _render_params("Attributes", parsed["Attributes"]) + _render_params("Returns", parsed["Returns"]) + + if parsed["Raises"]: + out.append("**Raises**") + out.append("") + for r in parsed["Raises"]: + name = r.name or "" + desc = " ".join(r.desc).strip() + out.append( + f"- **`{name}`** — {desc}" if desc else f"- **`{name}`**" + ) + out.append("") + if parsed["Notes"]: + out.append("**Notes**") + out.append("") + out.extend(parsed["Notes"]) + out.append("") + if parsed["Examples"]: + out.append("**Examples**") + out.append("") + code_lines = [ln for ln in parsed["Examples"] if ln.strip()] + if code_lines: + out.append("```python") + out.extend(parsed["Examples"]) + out.append("```") + out.append("") + + return "\n".join(out) + + +def _render_class(cls: dict, level: int = 2) -> str: + h = "#" * level + h2 = "#" * (level + 1) + lines = ["
\n", f"{h} class `{cls['name']}`\n"] + if cls.get("doc"): + lines.append(_doc_to_mdx_numpydoc(cls["doc"])) + lines.append("") + if cls.get("members"): + lines.append( + "**Members:** " + ", ".join(f"`{m}`" for m in cls["members"]) + ) + lines.append("") + for method in cls.get("methods", []): + name = method["name"] + sig = method["signature"] + lines.append(f"{h2} def `{name}`\n") + lines.append(f"```python\n{sig}\n```\n") + if method.get("doc"): + lines.append(_doc_to_mdx_numpydoc(method["doc"])) + lines.append("") + return "\n".join(lines) + + +def _render_function(fn: dict, level: int = 2) -> str: + h = "#" * level + lines = ["
\n", f"{h} def `{fn['name']}`\n"] + lines.append(f"```python\n{fn['signature']}\n```\n") + if fn.get("doc"): + lines.append(_doc_to_mdx_numpydoc(fn["doc"])) + lines.append("") + return "\n".join(lines) + + +def write_api_page(title: str, dest: Path, sections: list): + """ + sections: list of (heading, parsed_module_dict | None, [extra_mdx_lines]) + """ + lines = [f'---\ntitle: "{title}"\n---\n'] + for heading, parsed, extras in sections: + if heading: + lines.append(f"\n## {heading}\n") + if parsed: + if parsed.get("module_doc"): + lines.append(_doc_to_mdx_numpydoc(parsed["module_doc"]) + "\n") + for cls in parsed.get("classes", []): + lines.append(_render_class(cls, level=3)) + for fn in parsed.get("functions", []): + lines.append(_render_function(fn, level=3)) + for extra in extras or []: + lines.append(extra) + content = "\n".join(lines) + "\n" + content = re.sub(r"\\?<=", "<=", content) + dest.write_text(content, encoding="utf-8") + print(f" Written: {dest.relative_to(REPO_ROOT)}") + + +# --------------------------------------------------------------------------- +# Directory scanner +# --------------------------------------------------------------------------- + + +def _read_init_all(directory: Path) -> set[str] | None: + """Read __all__ from directory/__init__.py via ast.literal_eval. Returns None if absent.""" + init_path = directory / "__init__.py" + if not init_path.exists(): + return None + try: + tree = ast.parse(init_path.read_text(encoding="utf-8")) + except (SyntaxError, OSError): + return None + for node in tree.body: + if isinstance(node, ast.Assign): + for target in node.targets: + if isinstance(target, ast.Name) and target.id == "__all__": + try: + return set(ast.literal_eval(node.value)) + except Exception: + return None + return None + + +def _scan_dir( + src_dir: Path, + exclude: set, + use_init_all: bool = False, + order: list | None = None, +) -> list[tuple[str, dict]]: + """ + Return (section_heading, parsed_module) for every public .py and .pyx + file in src_dir (non-recursive). + + - Private modules (single leading underscore) and excluded stems are skipped. + - .py wins over .pyx for the same stem (Python wrapper preferred over Cython). + - When use_init_all=True, symbols not in src_dir/__init__.py __all__ are dropped. + - Cross-file deduplication: if the same symbol name appears in multiple files, + only the first occurrence (order-then-alphabetical) is kept. + - order: list of stem names controlling processing sequence; unlisted stems + go at the end in alphabetical order. + - *_wrapper.pyx files never emit a section heading (symbols appear inline). + """ + if not src_dir.exists(): + return [] + + allowed_symbols: set[str] | None = ( + _read_init_all(src_dir) if use_init_all else None + ) + + # Collect .py and .pyx files, deduplicated by stem (.py wins over .pyx) + seen_stems: dict[str, Path] = {} + for path in sorted(src_dir.iterdir()): + if path.suffix not in (".py", ".pyx"): + continue + if path.name.startswith("_") and not path.name.startswith("__"): + continue + if path.stem in exclude: + continue + if path.stem in seen_stems: + if path.suffix == ".py": + seen_stems[path.stem] = path + else: + seen_stems[path.stem] = path + + # Sort stems: items in `order` come first (in list order), rest alphabetically. + _order = order or [] + + def _sort_key(stem: str) -> tuple: + try: + return (0, _order.index(stem), stem) + except ValueError: + return (1, 0, stem) + + seen_symbol_names: set[str] = set() + results = [] + for stem, path in sorted( + seen_stems.items(), key=lambda kv: _sort_key(kv[0]) + ): + parsed = ( + _parse_module_pyx(path) + if path.suffix == ".pyx" + else _parse_module(path) + ) + + # Wrapper .pyx files expose C-layer internals as functions — drop them; + # only enums/classes defined there are public API. + if path.suffix == ".pyx" and "_wrapper" in path.stem: + parsed["functions"] = [] + + # Filter by __all__ if requested + if allowed_symbols is not None: + parsed["classes"] = [ + c for c in parsed["classes"] if c["name"] in allowed_symbols + ] + parsed["functions"] = [ + f for f in parsed["functions"] if f["name"] in allowed_symbols + ] + + # Cross-file dedup: drop symbols already emitted by an earlier file + parsed["classes"] = [ + c for c in parsed["classes"] if c["name"] not in seen_symbol_names + ] + parsed["functions"] = [ + f + for f in parsed["functions"] + if f["name"] not in seen_symbol_names + ] + seen_symbol_names.update(c["name"] for c in parsed["classes"]) + seen_symbol_names.update(f["name"] for f in parsed["functions"]) + + if not (parsed["classes"] or parsed["functions"]): + continue + + # Wrapper .pyx files don't get their own ## section heading — their + # symbols appear inline without a heading break. + is_wrapper_pyx = path.suffix == ".pyx" and "_wrapper" in path.stem + heading = ( + "" + if is_wrapper_pyx + else ( + src_dir.name.replace("_", " ").title() + if path.name == "__init__.py" + else stem.replace("_", " ").title() + ) + ) + results.append((heading, parsed)) + return results + + +# --------------------------------------------------------------------------- +# Navigation updater +# --------------------------------------------------------------------------- + +_PY_NAV_START = " - section: Routing Optimization\n" +_PY_NAV_SECTION = " - section: Python API Overview\n" + +# Marker comments in docs-v26-08.yml that bracket the auto-generated Python API pages. +# Insert these markers manually around the Python API page entries if you want +# update_python_api_navigation() to manage them. Without markers the function is a no-op. +_NAV_START_MARKER = " # BEGIN auto-generated python-api pages\n" +_NAV_END_MARKER = " # END auto-generated python-api pages\n" + + +def update_python_api_navigation(generated_pages: list[dict]) -> None: + """ + Rewrite the auto-generated Python API page entries in docs-v26-08.yml. + Only operates if the BEGIN/END marker comments are present in the file. + """ + if not DOCS_V26_08.exists(): + return + text = DOCS_V26_08.read_text(encoding="utf-8") + if _NAV_START_MARKER not in text or _NAV_END_MARKER not in text: + return # Markers not present; leave navigation alone + + start = text.index(_NAV_START_MARKER) + len(_NAV_START_MARKER) + end = text.index(_NAV_END_MARKER) + + new_entries = [] + for page in generated_pages: + rel_path = Path(page["output"]) + title = page["title"] + nav_path = f"docs/pages/{rel_path}" + new_entries.append(f" - page: {title}\n") + new_entries.append(f" path: {nav_path}\n") + + new_block = "".join(new_entries) + updated = text[:start] + new_block + text[end:] + DOCS_V26_08.write_text(updated, encoding="utf-8") + print(f" Updated navigation in {DOCS_V26_08.relative_to(REPO_ROOT)}") + + +# --------------------------------------------------------------------------- +# Page generator +# --------------------------------------------------------------------------- + + +def generate_pages(): + for page in PAGE_SOURCES: + sections = [] + for dir_cfg in page["dirs"]: + # dirs entries are either plain strings or dicts with path + options + if isinstance(dir_cfg, str): + dir_path = dir_cfg + exclude = set() + use_init_all = False + else: + dir_path = dir_cfg["path"] + exclude = set(dir_cfg.get("exclude", set())) + use_init_all = bool(dir_cfg.get("use_init_all", False)) + + order = ( + list(dir_cfg.get("order", [])) + if isinstance(dir_cfg, dict) + else [] + ) + src_dir = PYTHON_SRC / dir_path + for heading, parsed in _scan_dir( + src_dir, exclude, use_init_all, order + ): + sections.append((heading, parsed, [])) + + if not sections: + sections = [ + ( + "", + None, + [ + "No public API symbols found in the configured source directories." + ], + ) + ] + + dest = PAGES / page["output"] + dest.parent.mkdir(parents=True, exist_ok=True) + write_api_page(title=page["title"], dest=dest, sections=sections) + + update_python_api_navigation(PAGE_SOURCES) + + +if __name__ == "__main__": + print( + "Extracting Python API docstrings (AST + Cython regex, no imports)..." + ) + generate_pages() + print("Done.") diff --git a/fern/fern.config.json b/fern/fern.config.json new file mode 100644 index 0000000000..a1f9166813 --- /dev/null +++ b/fern/fern.config.json @@ -0,0 +1,4 @@ +{ + "organization": "nvidia-cuopt", + "version": "5.67.1" +} diff --git a/fern/generate_api_docs.py b/fern/generate_api_docs.py new file mode 100644 index 0000000000..b0361317df --- /dev/null +++ b/fern/generate_api_docs.py @@ -0,0 +1,136 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Generate dynamic API reference content for the Fern docs. + +Regenerates on every `./build.sh docs` run: + - fern/openapi/cuopt_spec.yaml (from FastAPI app) + - fern/docs/scripts/cuopt-install-version.js (version string for install widget) + - fern/docs/pages/cuopt-python/ (Python API MDX via AST) + - fern/docs/pages/cuopt-c/ (C API MDX from headers) + - *-examples.mdx pages (example .py/.c files embedded as code blocks) + +Hand-edited prose in MDX pages is preserved; only code-embed markers are refreshed. +""" + +import importlib.util +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).parent.parent +FERN = REPO_ROOT / "fern" + + +def _generate_openapi_spec(): + try: + sys.path.insert(0, str(REPO_ROOT / "python/cuopt_server")) + from cuopt_server.webserver import app + from fastapi.openapi.utils import get_openapi + import yaml + + spec = get_openapi( + title=app.title, + version=app.version, + openapi_version=app.openapi_version, + description=app.description, + summary=getattr(app, "summary", None), + routes=app.routes, + ) + + schemas = spec.get("components", {}).get("schemas", {}) + if "IdModel" in schemas: + schemas["IdModel"].pop("additionalProperties", None) + + for path_item in spec.get("paths", {}).values(): + for op in path_item.values(): + if not isinstance(op, dict): + continue + for resp in op.get("responses", {}).values(): + for media in resp.get("content", {}).values(): + for ex in media.get("examples", {}).values(): + val = ex.get("value", {}) + if ( + isinstance(val, dict) + and "error" in val + and "error_result" not in val + ): + val["error_result"] = False + + incumbents_path = "/cuopt/solution/{id}/incumbents" + inc_op = spec.get("paths", {}).get(incumbents_path, {}).get("get", {}) + inc_examples = ( + inc_op.get("responses", {}) + .get("200", {}) + .get("content", {}) + .get("application/json", {}) + .get("examples", {}) + ) + for ex in inc_examples.values(): + for item in ex.get("value") or []: + if isinstance(item, dict): + if not isinstance(item.get("cost"), (int, float)): + item["cost"] = None + if "bound" not in item: + item["bound"] = None + + out = FERN / "openapi/cuopt_spec.yaml" + out.parent.mkdir(parents=True, exist_ok=True) + with open(out, "w") as f: + yaml.dump( + spec, + f, + default_flow_style=False, + allow_unicode=True, + sort_keys=False, + ) + print( + f"Generated {out.relative_to(REPO_ROOT)} ({len(spec['paths'])} paths)" + ) + + ver = app.version + parts = ver.split(".") + major, minor = int(parts[0]), int(parts[1]) if len(parts) > 1 else 0 + pip_ver = f"{major}.{minor}" + conda_ver = f"{major:02d}.{minor:02d}" + version_js = FERN / "docs/scripts/cuopt-install-version.js" + version_js.parent.mkdir(parents=True, exist_ok=True) + version_js.write_text( + f'window.CUOPT_INSTALL_VERSION = {{"conda": "{conda_ver}", "pip": "{pip_ver}"}};\n' + ) + print( + f"Generated {version_js.relative_to(REPO_ROOT)} (conda={conda_ver}, pip={pip_ver})" + ) + + except Exception as e: + print(f" [WARN] Could not generate OpenAPI spec: {e}") + print( + " Ensure cuopt_server is installed: pip install -e python/cuopt_server --no-deps" + ) + + +def _run_module(path: Path, fn_name: str): + spec = importlib.util.spec_from_file_location(path.stem, path) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + getattr(mod, fn_name)() + + +def main(): + print("Generating OpenAPI spec and version script...") + _generate_openapi_spec() + + print("Extracting Python API docstrings...") + _run_module(FERN / "extract_python_api.py", "generate_pages") + + print("Extracting C API from headers...") + _run_module(FERN / "extract_c_api.py", "main") + + print("Embedding examples into MDX pages...") + _run_module(FERN / "embed_examples.py", "embed_all") + + print("\nDone. Run: fern check") + + +if __name__ == "__main__": + main() diff --git a/fern/generators.yml b/fern/generators.yml new file mode 100644 index 0000000000..fd9f7399cb --- /dev/null +++ b/fern/generators.yml @@ -0,0 +1,3 @@ +api: + specs: + - openapi: openapi/cuopt_spec.yaml diff --git a/fern/openapi/cuopt_spec.yaml b/fern/openapi/cuopt_spec.yaml new file mode 100644 index 0000000000..f5151702f8 --- /dev/null +++ b/fern/openapi/cuopt_spec.yaml @@ -0,0 +1,4273 @@ +openapi: 3.1.0 +info: + title: cuOpt Server + summary: OpenAPI Specification for cuOpt + version: '26.08' +paths: + /v2/health/live: + get: + summary: Health + description: To check liveness of the server + operationId: health_v2_health_live_get + responses: + '200': + description: Successful health check + content: + application/json: + schema: + $ref: '#/components/schemas/HealthResponseModel' + examples: + Healthy response: + value: + status: RUNNING + version: '26.08' + '500': + description: Unsuccessful health check + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + examples: + Unhealthy response: + value: + error: ' + + Status : Broken + + The server will be restarted and will be available in 15 mins + !!! + + ERROR : Server is BROKEN' + error_result: false + /v2/health/ready: + get: + summary: Health + description: To check readiness of the server + operationId: health_v2_health_ready_get + responses: + '200': + description: Successful health check + content: + application/json: + schema: + $ref: '#/components/schemas/HealthResponseModel' + examples: + Healthy response: + value: + status: RUNNING + version: '26.08' + '500': + description: Unsuccessful health check + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + examples: + Unhealthy response: + value: + error: ' + + Status : Broken + + The server will be restarted and will be available in 15 mins + !!! + + ERROR : Server is BROKEN' + error_result: false + /cuopt/health: + get: + summary: Health + description: To ping if server is running + operationId: health_cuopt_health_get + responses: + '200': + description: Successful health check + content: + application/json: + schema: + $ref: '#/components/schemas/HealthResponseModel' + examples: + Healthy response: + value: + status: RUNNING + version: '26.08' + '500': + description: Unsuccessful health check + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + examples: + Unhealthy response: + value: + error: ' + + Status : Broken + + The server will be restarted and will be available in 15 mins + !!! + + ERROR : Server is BROKEN' + error_result: false + /: + get: + summary: Health + description: To ping if server is running + operationId: health__get + responses: + '200': + description: Successful health check + content: + application/json: + schema: + $ref: '#/components/schemas/HealthResponseModel' + examples: + Healthy response: + value: + status: RUNNING + version: '26.08' + '500': + description: Unsuccessful health check + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + examples: + Unhealthy response: + value: + error: ' + + Status : Broken + + The server will be restarted and will be available in 15 mins + !!! + + ERROR : Server is BROKEN' + error_result: false + /cuopt/log/{id}: + get: + summary: Query solver logs by id self-hosted + description: 'Note: This is for self-hosted. Query solver log. The ''id'' is + the uuid returned when the request was made.' + operationId: getsolverlogs_cuopt_log__id__get + parameters: + - name: id + in: path + required: true + schema: + type: string + title: Id + - name: frombyte + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: Indicates the position to start log read + default: 0 + title: Frombyte + description: Indicates the position to start log read + - name: accept + in: header + required: false + schema: + type: string + description: 'Supported result mime_types are ''application/json'', ''application/vnd.msgpack'', + ''application/zlib'', and standard mime_type wildcards. ' + default: application/json + title: Accept + description: 'Supported result mime_types are ''application/json'', ''application/vnd.msgpack'', + ''application/zlib'', and standard mime_type wildcards. ' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/LogResponseModel' + '415': + description: Unsupported mime_type specified + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '422': + description: Unprocessable Entity or Runtime Error or Out of memory error + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '500': + description: Any uncaught cuOpt error or Server errors + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + delete: + summary: Delete solver logs by id (self-hosted) + description: 'Note: This is for self-hosted. Delete accumulated logs for a particular + request.' + operationId: deletesolverlogs_cuopt_log__id__delete + parameters: + - name: id + in: path + required: true + schema: + type: string + title: Id + - name: accept + in: header + required: false + schema: + type: string + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. This applies to + exception messages returned by this request. + default: application/json + title: Accept + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. This applies to exception + messages returned by this request. + responses: + '200': + description: Successful request response (no content) + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponseModel' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '422': + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '500': + description: Any uncaught cuOpt error or Server errors + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + /cuopt/solution/{id}/incumbents: + get: + summary: Get incumbent solutions for MIP (self-hosted) + description: 'Note: for use with self-hosted cuOpt instances. Return incumbent + solutions from the MIP solver produced for this id since the last GET. Result + will be a list of the form [{''solution'': [1.0, 1.0], ''cost'': 2.0, ''bound'': + 1.5}] where each item contains the fields ''solution'' (a list of floats), + ''cost'' (a float), and ''bound'' (a float or None when no finite bound is + available yet). An empty list indicates that there are no current incumbent + solutions at this time. A sentinel value of [{''solution'': [], ''cost'': + None, ''bound'': None}] indicates that no future incumbent values will be + produced. The ''id'' is the reqId value returned from a POST to /cuopt/request' + operationId: getincumbent_cuopt_solution__id__incumbents_get + parameters: + - name: id + in: path + required: true + schema: + type: string + title: Id + - name: accept + in: header + required: false + schema: + type: string + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. If a wildcard is + used, the accept mime_type will be set to the content_type mime_type of + the original request. + default: application/json + title: Accept + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. If a wildcard is used, + the accept mime_type will be set to the content_type mime_type of the original + request. + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IncumbentSolution' + title: Response Getincumbent Cuopt Solution Id Incumbents Get + examples: + Empty response (no current solutions): + value: [] + Sentinel response (no future solutions).: + value: + - solution: [] + cost: null + bound: null + Solution response: + value: + - solution: + - 1.0 + - 2.0 + cost: 3.0 + bound: null + - solution: + - 4.0 + - 5.0 + cost: 6.0 + bound: null + '415': + description: Unsupported mime_type specified + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '422': + description: Unprocessable Entity or Runtime Error or Out of memory error + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '500': + description: Any uncaught cuOpt error or Server errors + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + /cuopt/solution: + post: + summary: Upload a routing solution only(self-hosted) + description: 'Note: for use with self-hosted cuOpt instances. Accepts a cuOpt + solution and returns a reqId. This is useful for uploading a VRP solution + to use as an initial solution for another request.' + operationId: postsolution_cuopt_solution_post + parameters: + - name: accept + in: header + required: false + schema: + type: string + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. If a wildcard is + used, the accept mime_type will be set to the content_type mime_type + default: application/json + title: Accept + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. If a wildcard is used, + the accept mime_type will be set to the content_type mime_type + - name: content-type + in: header + required: false + schema: + type: string + description: Supported content mime_types are 'application/json', 'application/vnd.msgpack', + and 'application/zlib' + default: application/json + title: Content-Type + description: Supported content mime_types are 'application/json', 'application/vnd.msgpack', + and 'application/zlib' + - name: content-length + in: header + required: true + schema: + type: integer + title: Content-Length + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/IdModel' + examples: + Id response: + value: + reqId: e8421e9e-e42e-4511-8da2-314253667dcf + '400': + description: Value Error Or Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '415': + description: Unsupported mime_type specified + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '422': + description: Unprocessable Entity or Runtime Error or Out of memory error + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '500': + description: Any uncaught cuOpt error or Server errors + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + requestBody: + content: + application/json: + schema: + additionalProperties: false + properties: + response: + anyOf: + - additionalProperties: false + properties: + solver_response: + additionalProperties: false + properties: + status: + default: 0 + description: "0 - Solution is available \n1 - Infeasible\ + \ solution is available \n" + examples: + - 0 + title: Status + type: integer + num_vehicles: + default: -1 + description: Number of vehicle being used for the solution + examples: + - 2 + title: Num Vehicles + type: integer + solution_cost: + default: -1.0 + description: Total cost of the solution + examples: + - 2 + title: Solution Cost + type: number + objective_values: + additionalProperties: + type: number + default: {} + description: Individual objective values + examples: + - objective_values: + cost: 100.0 + prize: -1000.0 + travel_time: 245.0 + title: Objective Values + type: object + vehicle_data: + additionalProperties: + additionalProperties: false + properties: + task_id: + default: [] + description: task_ids being assigned to vehicle + along with depot and breaks + items: + type: string + title: Task Id + type: array + arrival_stamp: + default: [] + description: arrival stamps at each task locations + items: + type: number + title: Arrival Stamp + type: array + route: + default: [] + description: Route indices as per waypoint graph + or cost matrix provided + items: + type: integer + title: Route + type: array + type: + default: [] + description: "Type of routing point, whether it\ + \ is Depot, Waypoint - w \nDelivery, Break, Pickup\ + \ \n" + items: + enum: + - Depot + - Pickup + - Delivery + - Break + - w + title: LocationTypeEnum + type: string + title: Type + type: array + title: VehicleData + type: object + default: {} + description: All the details of vehicle routes and timestamps + examples: + - vehicle_data: + veh-1: + arrival_stamp: + - 1 + - 2 + route: + - 1 + - 1 + task_id: + - Break + - Task-A + type: + - Break + - Delivery + veh-2: + arrival_stamp: + - 2 + - 2 + - 4 + - 5 + route: + - 0 + - 0 + - 2 + - 0 + task_id: + - Depot + - Break + - Task-B + - Depot + type: + - Depot + - Break + - Delivery + - Depot + title: Vehicle Data + type: object + initial_solutions: + default: [] + description: Indicates whether each initial solution was + accepted, not accepted or not evaluated by the solver + in case initial solutions were provided in request. + items: + type: string + title: Initial Solutions + type: array + dropped_tasks: + additionalProperties: false + properties: + task_id: + anyOf: + - items: + type: integer + type: array + - items: + type: string + type: array + default: [] + description: With prize collection enabled, there + is a chance of tasks being dropped to make a feasible + solution. This list contains infeasible task ids + which are dropped. + title: Task Id + task_index: + default: [] + description: With prize collection enabled, there + is a chance of tasks being dropped to make a feasible + solution. This list contains infeasible task indices + into task locations which are dropped. + items: + type: integer + title: Task Index + type: array + title: DroppedTasks + type: object + msg: + anyOf: + - type: string + - type: 'null' + default: '' + description: Any information pertaining to the run. + title: Msg + title: FeasibleResultData + type: object + perf_times: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + description: Etl and Solve times of the solve call + title: Perf Times + title: FeasibleSolve + type: object + - additionalProperties: false + properties: + solver_infeasible_response: + additionalProperties: false + properties: + status: + default: 1 + description: "1 - Infeasible solution is available \n" + examples: + - 1 + title: Status + type: integer + num_vehicles: + default: -1 + description: Number of vehicle being used for the solution + examples: + - 2 + title: Num Vehicles + type: integer + solution_cost: + default: -1.0 + description: Total cost of the solution + examples: + - 2 + title: Solution Cost + type: number + objective_values: + additionalProperties: + type: number + default: {} + description: Individual objective values + examples: + - objective_values: + cost: 100.0 + prize: -1000.0 + travel_time: 245.0 + title: Objective Values + type: object + vehicle_data: + additionalProperties: + additionalProperties: false + properties: + task_id: + default: [] + description: task_ids being assigned to vehicle + along with depot and breaks + items: + type: string + title: Task Id + type: array + arrival_stamp: + default: [] + description: arrival stamps at each task locations + items: + type: number + title: Arrival Stamp + type: array + route: + default: [] + description: Route indices as per waypoint graph + or cost matrix provided + items: + type: integer + title: Route + type: array + type: + default: [] + description: "Type of routing point, whether it\ + \ is Depot, Waypoint - w \nDelivery, Break, Pickup\ + \ \n" + items: + enum: + - Depot + - Pickup + - Delivery + - Break + - w + title: LocationTypeEnum + type: string + title: Type + type: array + title: VehicleData + type: object + default: {} + description: All the details of vehicle routes and timestamps + examples: + - vehicle_data: + veh-1: + arrival_stamp: + - 1 + - 2 + route: + - 1 + - 1 + task_id: + - Break + - Task-A + type: + - Break + - Delivery + veh-2: + arrival_stamp: + - 2 + - 2 + - 4 + - 5 + route: + - 0 + - 0 + - 2 + - 0 + task_id: + - Depot + - Break + - Task-B + - Depot + type: + - Depot + - Break + - Delivery + - Depot + title: Vehicle Data + type: object + initial_solutions: + default: [] + description: Indicates whether each initial solution was + accepted, not accepted or not evaluated by the solver + in case initial solutions were provided in request. + items: + type: string + title: Initial Solutions + type: array + dropped_tasks: + additionalProperties: false + properties: + task_id: + anyOf: + - items: + type: integer + type: array + - items: + type: string + type: array + default: [] + description: With prize collection enabled, there + is a chance of tasks being dropped to make a feasible + solution. This list contains infeasible task ids + which are dropped. + title: Task Id + task_index: + default: [] + description: With prize collection enabled, there + is a chance of tasks being dropped to make a feasible + solution. This list contains infeasible task indices + into task locations which are dropped. + items: + type: integer + title: Task Index + type: array + title: DroppedTasks + type: object + msg: + anyOf: + - type: string + - type: 'null' + default: '' + description: Any information pertaining to the run. + title: Msg + title: InfeasibleResultData + type: object + perf_times: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + description: Etl and Solve times of the solve call + title: Perf Times + title: InFeasibleSolve + type: object + - additionalProperties: false + properties: + solver_response: + anyOf: + - additionalProperties: false + properties: + status: + default: NoTermination + description: "In case of LP : \n\nNoTermination - No\ + \ Termination \n\nNumericalError - Numerical Error\ + \ \n\nOptimal - Optimal solution is available \n\n\ + PrimalInfeasible - Primal Infeasible solution \n\n\ + DualInfeasible - Dual Infeasible solution \n\nIterationLimit\ + \ - Iteration Limit reached \n\nTimeLimit - TimeLimit\ + \ reached \n\nPrimalFeasible - Primal Feasible \n\n\ + ---------------------- \n\nIn case of MILP/IP : \n\ + \nNoTermination - No Termination \n\nOptimal - Optimal\ + \ solution is available \n\nFeasibleFound - Feasible\ + \ solution is available \n\nInfeasible - Infeasible\ + \ \n\nUnbounded - Unbounded \n\nTimeLimit - TimeLimit\ + \ reached \n\n" + examples: + - Optimal + title: Status + solution: + additionalProperties: false + properties: + problem_category: + description: Category of the solution, LP-0/MIP-1/IP-2 + title: Problem Category + type: integer + primal_solution: + default: [] + description: Primal solution of the LP problem + items: + type: number + title: Primal Solution + type: array + dual_solution: + default: [] + description: "Note: Only applicable to LP \nDual\ + \ solution of the LP problem\n" + items: + type: number + title: Dual Solution + type: array + solver_time: + description: Returns the engine solve time in seconds + title: Solver Time + type: number + solved_by: + description: Returns whether problem was solved + by PDLP, Barrier or Dual Simplex + title: Solved By + type: integer + primal_objective: + description: Primal objective of the LP problem + title: Primal Objective + type: number + dual_objective: + description: "Note: Only applicable to LP \nDual\ + \ objective of the LP problem \n" + title: Dual Objective + type: number + vars: + additionalProperties: true + default: {} + description: Dictionary mapping each variable (name) + to its value + title: Vars + type: object + lp_statistics: + additionalProperties: true + default: {} + description: "Note: Only applicable to LP \nConvergence\ + \ statistics of the solution \nIncludes primal\ + \ residual, dual residual, \nreduced cost and\ + \ gap \n" + title: Lp Statistics + type: object + milp_statistics: + additionalProperties: true + default: {} + description: "Note: Only applicable to MILP \nConvergence\ + \ statistics of the solution \nIncludes mip gap,\ + \ solution_bound, pre-solve time, \nmax_constraint_violation,\ + \ max_int_violatione and \n max_variable_bound_violation\ + \ \n" + title: Milp Statistics + type: object + title: SolutionData + type: object + title: SolutionResultData + type: object + - items: + additionalProperties: false + properties: + status: + default: NoTermination + description: "In case of LP : \n\nNoTermination -\ + \ No Termination \n\nNumericalError - Numerical\ + \ Error \n\nOptimal - Optimal solution is available\ + \ \n\nPrimalInfeasible - Primal Infeasible solution\ + \ \n\nDualInfeasible - Dual Infeasible solution\ + \ \n\nIterationLimit - Iteration Limit reached \n\ + \nTimeLimit - TimeLimit reached \n\nPrimalFeasible\ + \ - Primal Feasible \n\n---------------------- \n\ + \nIn case of MILP/IP : \n\nNoTermination - No Termination\ + \ \n\nOptimal - Optimal solution is available \n\ + \nFeasibleFound - Feasible solution is available\ + \ \n\nInfeasible - Infeasible \n\nUnbounded - Unbounded\ + \ \n\nTimeLimit - TimeLimit reached \n\n" + examples: + - Optimal + title: Status + solution: + additionalProperties: false + properties: + problem_category: + description: Category of the solution, LP-0/MIP-1/IP-2 + title: Problem Category + type: integer + primal_solution: + default: [] + description: Primal solution of the LP problem + items: + type: number + title: Primal Solution + type: array + dual_solution: + default: [] + description: "Note: Only applicable to LP \nDual\ + \ solution of the LP problem\n" + items: + type: number + title: Dual Solution + type: array + solver_time: + description: Returns the engine solve time in + seconds + title: Solver Time + type: number + solved_by: + description: Returns whether problem was solved + by PDLP, Barrier or Dual Simplex + title: Solved By + type: integer + primal_objective: + description: Primal objective of the LP problem + title: Primal Objective + type: number + dual_objective: + description: "Note: Only applicable to LP \nDual\ + \ objective of the LP problem \n" + title: Dual Objective + type: number + vars: + additionalProperties: true + default: {} + description: Dictionary mapping each variable + (name) to its value + title: Vars + type: object + lp_statistics: + additionalProperties: true + default: {} + description: "Note: Only applicable to LP \nConvergence\ + \ statistics of the solution \nIncludes primal\ + \ residual, dual residual, \nreduced cost and\ + \ gap \n" + title: Lp Statistics + type: object + milp_statistics: + additionalProperties: true + default: {} + description: "Note: Only applicable to MILP \n\ + Convergence statistics of the solution \nIncludes\ + \ mip gap, solution_bound, pre-solve time, \n\ + max_constraint_violation, max_int_violatione\ + \ and \n max_variable_bound_violation \n" + title: Milp Statistics + type: object + title: SolutionData + type: object + title: SolutionResultData + type: object + type: array + default: + status: NoTermination + solution: + dual_solution: [] + lp_statistics: {} + milp_statistics: {} + primal_solution: [] + vars: {} + description: LP solution + title: Solver Response + perf_times: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + description: Etl and Solve times of the solve call + title: Perf Times + total_solve_time: + anyOf: + - type: number + - type: 'null' + description: Total Solve time of batch problem + title: Total Solve Time + title: LPSolve + type: object + description: Solution + title: Response + warnings: + default: [] + description: List of warnings for users to handle issues + items: + type: string + title: Warnings + type: array + notes: + default: [] + description: Any notes for users + items: + type: string + title: Notes + type: array + reqId: + description: Id of request + example: d6e60f36-8c3a-488b-a70b-a8c9b8cce377 + title: Reqid + type: string + required: + - response + - reqId + title: SolutionModelWithId + type: object + required: true + /cuopt/solution/{id}: + delete: + summary: Delete a solution by id (self-hosted) + description: 'Note: for use with self-hosted cuOpt instances. Delete a solution + by id. The ''id'' is the reqId value returned from a POST to /cuopt/request + or /cuopt/solution.' + operationId: deletesolution_cuopt_solution__id__delete + parameters: + - name: id + in: path + required: true + schema: + type: string + description: 'ID of the solution to delete. ' + title: Id + description: 'ID of the solution to delete. ' + - name: accept + in: header + required: false + schema: + type: string + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. This applies to + exception messages returned by this request. + default: application/json + title: Accept + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. This applies to exception + messages returned by this request. + responses: + '200': + description: Successful request response (no content) + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponseModel' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '422': + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '500': + description: Any uncaught cuOpt error or Server errors + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + get: + summary: Get a solution by id (self-hosted) + description: 'Note: for use with cuOpt self-hosted instances. Get a solution + by id. The ''id'' is the reqId value returned from a POST to /cuopt/request + or /cuopt/solution. If the solution is generated by a POST to /cuopt/request + and the request has not yet completed, the reqId value will be returned and + can be used to continue polling.' + operationId: getsolution_cuopt_solution__id__get + parameters: + - name: id + in: path + required: true + schema: + type: string + title: Id + - name: accept + in: header + required: false + schema: + type: string + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. If a wildcard is + used, the accept mime_type will be set to the content_type mime_type of + the original request. + default: application/json + title: Accept + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. If a wildcard is used, + the accept mime_type will be set to the content_type mime_type of the original + request. + responses: + '200': + description: Successful request response + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SolutionModelWithId' + - $ref: '#/components/schemas/SolutionModelInFile' + - $ref: '#/components/schemas/IdModel' + title: Response Getsolution Cuopt Solution Id Get + examples: + VRP response: + value: + response: + solver_response: + status: 0 + num_vehicles: 2 + solution_cost: 2.0 + objective_values: + cost: 2.0 + vehicle_data: + veh-1: + task_id: + - Break + - Task-A + arrival_stamp: + - 1.0 + - 2.0 + type: + - Break + - Delivery + route: + - 1 + - 1 + veh-2: + task_id: + - Depot + - Break + - Task-B + - Depot + arrival_stamp: + - 2.0 + - 2.0 + - 4.0 + - 5.0 + type: + - Depot + - Break + - Delivery + - Depot + route: + - 0 + - 0 + - 2 + - 0 + dropped_tasks: + task_id: [] + task_index: [] + reqId: e8421e9e-e42e-4511-8da2-314253667dcf + LP response: + value: + response: + solver_response: + status: Optimal + solution: + problem_category: 0 + primal_solution: + - 0.0 + - 0.0 + dual_solution: + - 0.0 + - 0.0 + primal_objective: 0.0 + dual_objective: 0.0 + solver_time: 43.0 + vars: {} + lp_statistics: + primal_residual: 0.0 + dual_residual: 0.0 + gap: 0.0 + reduced_cost: + - 0.2 + - 0.1 + mip_statistics: {} + reqId: 644dac46-5198-4293-ae3e-0f02a6d41863 + MILP response: + value: + response: + solver_response: + status: FeasibleFound + solution: + problem_category: 1 + primal_solution: + - 0.0 + - 0.0 + primal_objective: 0.0 + solver_time: 43.0 + vars: {} + lp_statistics: {} + mip_statistics: + mip_gap: 0.0 + presolve_time: 0.0 + solution_bound: 0.0 + max_constraint_violation: 0.0 + max_int_violation: 0.0 + max_variable_bound_violation: 0.0 + reqId: 644dac46-5198-4293-ae3e-0f02a6d41863 + ID response: + value: + reqId: e8421e9e-e42e-4511-8da2-314253667dcf + application/vnd.msgpack: + schema: + type: string + format: byte + examples: + VRP response compressed with msgpack: + value: \x82\xa8response\x81\xafsolver_response\x86\xa6status\x00\xacnum_vehicles\x02\xadsolution_cost\xcb@\x00\x00\x00\x00\x00\x00\x00\xb0objective_values\x81\xa4cost\xcb@\x00\x00\x00\x00\x00\x00\x00\xacvehicle_data\x82\xa5veh-1\x84\xa7task_id\x92\xa5Break\xa6Task-A\xadarrival_stamp\x92\xcb?\xf0\x00\x00\x00\x00\x00\x00\xcb@\x00\x00\x00\x00\x00\x00\x00\xa4type\x92\xa5Break\xa8Delivery\xa5route\x92\x01\x01\xa5veh-2\x84\xa7task_id\x94\xa5Depot\xa5Break\xa6Task-B\xa5Depot\xadarrival_stamp\x94\xcb@\x00\x00\x00\x00\x00\x00\x00\xcb@\x00\x00\x00\x00\x00\x00\x00\xcb@\x10\x00\x00\x00\x00\x00\x00\xcb@\x14\x00\x00\x00\x00\x00\x00\xa4type\x94\xa5Depot\xa5Break\xa8Delivery\xa5Depot\xa5route\x94\x00\x00\x02\x00\xaddropped_tasks\x82\xa7task_id\x90\xaatask_index\x90\xa5reqId\xd9$9147524d-c5fb-4413-976d-752e84a39123 + LP response compressed with msgpack: + value: \x83\xa8response\x81\xafsolver_response\x82\xa6status\x01\xa8solution\x87\xafprimal_solution\x92\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xaddual_solution\x92\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xb0primal_objective\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xaedual_objective\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xabsolver_time\xcb@\x08\x00\x00\x00\x00\x00\x00\xa4vars\x80\xadlp_statistics\x84\xafprimal_residual\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xaddual_residual\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xa3gap\xcb\x00\x00\x00\x00\x00\x00\x00\x00\xacreduced_cost\x92\xcb?\xc9\x99\x99\x99\x99\x99\x9a\xcb?\xb9\x99\x99\x99\x99\x99\x9a\xa5reqId\xd9$d47dfa94-e9f4-4207-ba70-a07775a3b044\xa5notes\x91\xa7Optimal + application/zlib: + schema: + type: string + format: byte + examples: + VRP response compressed with zlib: + value: x\x01mQ\xc1n\x830\x0c\xfd\x15\x94s3\x01*\x08v[\xd5\xcb\xee\xbbM(\xf2\x82\xab\xb1R\x92%\x01\xadB\xfc\xfb\xec\xac\xea\xca\xbaKd=?\xfb=\xbf\xcc\xc2\xa1\xb7f\xf0(\x1e\x93Yx\xd3O\xe8\xd4\x1a\x0b\x10FO\xedt\x93\x88a<\xa9\t\xdf;\xdd#C9A43\x86\xce\x0cJ\x1b\x1f\x18{`\xa2y\xfb@\x1d\xba\t\xd5\x04\xfd\x18\xc9\xb3\xf8e,D\xb9\xecQ-\x04\x88\xea\x04\xc8,V\x01\xfcQu-\xd5\xafb\xe7\x10\x8e\x82\xf8/\x04\xca'\xd1P\t\xceu\xb4W\xf9\x00'\xcb\xac\x8cEI\x99\x9b\xe1l\xf9\x9a\x9b\xc9=\xf6\xe4\xc4\x9d\xe3\xac3c\x88\xfdl\x93d\xcd\xc5\x88\xcc\xeft\xf7hM`\xdd\xb5\x81\x1dC?\xbd\x7f\x9d\xc4\xf3\xe3\xb3eO\xc5\x1fO\xf7[\xaf\xe6V{\xaf.i\x07\x9fFo\xb3\xb0\xd9\xd6\x19k\xb1U\x1c\x11\x7f\xc1,n\xc2\x8a\xe7\xc7\xec\x86\x16\xbf8\x04\x1a\xe2)\x87\x9f\xcf\x1c\xa78\x949b\x05Z\x96\x95\xd6r[\xa7 + \xeb\xbc,dq\xc0\xba\xd6e\x9eBQ\x89\xe5\x1b\xe6o\xa1\x12 + LP response compressed with zlib: + value: x\x01}OK\n\x830\x14\xbc\x8adm\x8a\x9fjKo\xd0U\x0f "\xf9= maxresult + title: Cuopt-Result-File + description: Result file name if output dir is enabled and size >= maxresult + - name: client-version + in: header + required: false + schema: + type: string + description: cuOpt client version. Set to 'custom' to skip version check + title: Client-Version + description: cuOpt client version. Set to 'custom' to skip version check + - name: accept + in: header + required: false + schema: + type: string + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. If a wildcard is + used, the accept mime_type will be set to the content_type mime_type + default: application/json + title: Accept + description: Supported result mime_types are 'application/json', 'application/vnd.msgpack', + 'application/zlib', and standard mime_type wildcards. If a wildcard is used, + the accept mime_type will be set to the content_type mime_type + - name: content-type + in: header + required: false + schema: + type: string + description: Supported content mime_types are 'application/json', 'application/vnd.msgpack', + and 'application/zlib' + default: application/json + title: Content-Type + description: Supported content mime_types are 'application/json', 'application/vnd.msgpack', + and 'application/zlib' + - name: content-length + in: header + required: true + schema: + type: integer + title: Content-Length + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/IdModel' + examples: + Id response: + value: + reqId: e8421e9e-e42e-4511-8da2-314253667dcf + '400': + description: Value Error Or Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '415': + description: Unsupported mime_type specified + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '422': + description: Unprocessable Entity or Runtime Error or Out of memory error + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + '500': + description: Any uncaught cuOpt error or Server errors + content: + application/json: + schema: + $ref: '#/components/schemas/DetailModel' + requestBody: + content: + application/json: + schema: + oneOf: + - additionalProperties: false + properties: + cost_waypoint_graph_data: + anyOf: + - additionalProperties: false + properties: + waypoint_graph: + anyOf: + - additionalProperties: + additionalProperties: false + properties: + edges: + description: "dtype: int32, edge >= 0. \n\n Vertices\ + \ of all the directed edges." + items: + type: integer + title: Edges + type: array + offsets: + description: "dtype: int32, offset >= 0. \n\n Offsets\ + \ which provide number of edges from the source\ + \ vertex signified by the index." + items: + type: integer + title: Offsets + type: array + weights: + anyOf: + - items: + type: number + type: array + - type: 'null' + description: "dtype: float32, weight >= 0. \n\n\ + \ Weights of each edges." + title: Weights + required: + - edges + - offsets + title: WaypointGraph + type: object + type: object + - type: 'null' + title: Waypoint Graph + title: WaypointGraphData + type: object + - type: 'null' + default: {} + description: "Waypoint graph with weights as cost to travel from\ + \ A to B \nand B to A. If there are different types of vehicles\ + \ \nthey can be provided with key value pair \nwhere key is\ + \ vehicle-type and value is the graph. Value of \nvehicle type\ + \ should be within [0, 255]" + examples: + - null + travel_time_waypoint_graph_data: + anyOf: + - additionalProperties: false + properties: + waypoint_graph: + anyOf: + - additionalProperties: + additionalProperties: false + properties: + edges: + description: "dtype: int32, edge >= 0. \n\n Vertices\ + \ of all the directed edges." + items: + type: integer + title: Edges + type: array + offsets: + description: "dtype: int32, offset >= 0. \n\n Offsets\ + \ which provide number of edges from the source\ + \ vertex signified by the index." + items: + type: integer + title: Offsets + type: array + weights: + anyOf: + - items: + type: number + type: array + - type: 'null' + description: "dtype: float32, weight >= 0. \n\n\ + \ Weights of each edges." + title: Weights + required: + - edges + - offsets + title: WaypointGraph + type: object + type: object + - type: 'null' + title: Waypoint Graph + title: WaypointGraphData + type: object + - type: 'null' + default: {} + description: "Waypoint graph with weights as time to travel from\ + \ A to B \nand B to A. If there are different types of vehicles\ + \ \nthey can be provided with key value pair \nwhere key is\ + \ vehicle-type and value is the graph. Value of \nvehicle type\ + \ should be within [0, 255]" + examples: + - null + cost_matrix_data: + anyOf: + - additionalProperties: false + properties: + data: + anyOf: + - additionalProperties: + items: + items: + type: number + type: array + type: array + type: object + - type: 'null' + description: "dtype : vehicle-type (uint8), cost (float32),\ + \ cost >= 0.\n \n\n Sqaure matrix with cost to travel\ + \ from A to B and B to A. \nIf there different types of\ + \ vehicles which have different \ncost matrices, they\ + \ can be provided with key value pair \nwhere key is vehicle-type\ + \ and value is cost matrix. Value of \nvehicle type should\ + \ be within [0, 255]" + title: Data + title: CostMatrices + type: object + - type: 'null' + default: {} + description: "Sqaure matrix with cost to travel from A to B and\ + \ B to A. \nCost is defined by user, it can be distance/fuel/time\ + \ or \na function of several factors.If there are different\ + \ types of vehicles which have different \ncost matrices, they\ + \ can be provided with key value pair \nwhere key is vehicle-type\ + \ and value is cost matrix. Value of \nvehicle type should be\ + \ within [0, 255]" + examples: + - cost_matrix: + '1': + - - 0 + - 1 + - 1 + - - 1 + - 0 + - 1 + - - 1 + - 1 + - 0 + '2': + - - 0 + - 1 + - 1 + - - 1 + - 0 + - 1 + - - 1 + - 2 + - 0 + travel_time_matrix_data: + anyOf: + - additionalProperties: false + properties: + data: + anyOf: + - additionalProperties: + items: + items: + type: number + type: array + type: array + type: object + - type: 'null' + description: "dtype : vehicle-type (uint8), cost (float32),\ + \ cost >= 0.\n \n\n Sqaure matrix with cost to travel\ + \ from A to B and B to A. \nIf there different types of\ + \ vehicles which have different \ncost matrices, they\ + \ can be provided with key value pair \nwhere key is vehicle-type\ + \ and value is cost matrix. Value of \nvehicle type should\ + \ be within [0, 255]" + title: Data + title: CostMatrices + type: object + - type: 'null' + default: {} + description: "Sqaure matrix with time to travel from A to B and\ + \ B to A. \nIf there are different types of vehicles which have\ + \ different \ntravel time matrices, they can be provided with\ + \ key value pair \nwhere key is vehicle-type and value is time\ + \ matrix. Value of \nvehicle type should be within [0, 255]" + examples: + - cost_matrix: + '1': + - - 0 + - 1 + - 1 + - - 1 + - 0 + - 1 + - - 1 + - 1 + - 0 + '2': + - - 0 + - 1 + - 1 + - - 1 + - 0 + - 1 + - - 1 + - 2 + - 0 + fleet_data: + additionalProperties: false + properties: + vehicle_locations: + description: "dtype: int32, vehicle_location >= 0. \n\n Start\ + \ and end location of the vehicles in the given set of locations\ + \ in WayPointGraph or CostMatrices.\nExample: For 2 vehicles,\ + \ \n\n [ \n\n [veh_1_start_loc, veh_1_end_loc],\ + \ \n\n [veh_2_start_loc, veh_2_end_loc] \n\n \ + \ ]" + examples: + - - - 0 + - 0 + - - 0 + - 0 + items: + items: + type: integer + type: array + title: Vehicle Locations + type: array + vehicle_ids: + anyOf: + - items: + type: string + type: array + - type: 'null' + description: List of the vehicle ids or names provided as + a string. Must be unique; duplicates are not allowed. + examples: + - - veh-1 + - veh-2 + title: Vehicle Ids + capacities: + anyOf: + - items: + items: + type: integer + type: array + type: array + - type: 'null' + description: "dtype: int32, capacity >= 0. \n\n Note: For\ + \ this release number of capacity dimensions are limited\ + \ to 3. \n\n Lists of capacities of each vehicle.\nMultiple\ + \ capacities can be added and each list will represent one\ + \ kind of capacity. Order of kind of the capacities should\ + \ match order of the demands.\nTotal capacity for each type\ + \ should be sufficient to complete all demand of that type.Example:\ + \ In case of two sets of capacities per vehicle with 3 vehicles,\ + \ \n\n [ \n\n [cap_1_veh_1, cap_1_veh_2, cap_1_veh_3],\ + \ \n\n [cap_2_veh_1, cap_2_veh_2, cap_2_veh_3] \n\ + \n ]" + examples: + - - - 2 + - 2 + - - 4 + - 1 + title: Capacities + vehicle_time_windows: + anyOf: + - items: + items: + type: integer + type: array + type: array + - type: 'null' + description: "dtype: int32, time >= 0. \n\n Earliest and Latest\ + \ time window pairs for each vehicle,\nfor example the data\ + \ would look as follows for 2 vehicles, \n \n\n [ \n\ + \n [veh_1_earliest, veh_1_latest], \n\n \ + \ [veh_2_earliest, veh_2_latest] \n\n ]" + examples: + - - - 0 + - 10 + - - 0 + - 10 + title: Vehicle Time Windows + vehicle_break_time_windows: + anyOf: + - items: + items: + items: + type: integer + type: array + type: array + type: array + - type: 'null' + description: "dtype: int32, time >= 0. \n\n Multiple break\ + \ time windows can be added for each vehicle.Earliest and\ + \ Latest break time window pairs for each vehicle,\nFor\ + \ example, in case of 2 sets of breaks for each vehicle\ + \ which translates to 2 dimensions of breaks,\n \n\n \ + \ [ \n\n [[brk_1_veh_1_earliest, brk_1_veh_1_latest],\ + \ [brk_1_veh_2_earliest, brk_1_veh_2_latest]] \n\n \ + \ [[brk_2_veh_1_earliest, brk_2_veh_1_latest], [brk_2_veh_2_earliest,\ + \ brk_2_veh_2_latest]] \n\n ] \n\n The break duration\ + \ within this time window is provided through vehicle_break_durations." + examples: + - - - - 1 + - 2 + - - 2 + - 3 + title: Vehicle Break Time Windows + vehicle_break_durations: + anyOf: + - items: + items: + type: integer + type: array + type: array + - type: 'null' + description: "dtype: int32, time >= 0. \n\n Break duration\ + \ for each vehicle. vehicle_break_time_windows should be\ + \ provided to use this option.For example, in case of having\ + \ 2 breaks for each vehicle, \n\n [ \n\n [brk_1_veh_1_duration,\ + \ brk_1_veh_2_duration], \n\n [brk_2_veh_1_duration,\ + \ brk_2_veh_2_duration], \n\n ]" + examples: + - - - 1 + - 1 + title: Vehicle Break Durations + vehicle_break_locations: + anyOf: + - items: + type: integer + type: array + - type: 'null' + description: "dtype: int32, location >= 0. \n\n Break location\ + \ where vehicles can take breaks. If not set, any location\ + \ can be used for the break." + examples: + - - 0 + - 1 + title: Vehicle Break Locations + vehicle_breaks: + anyOf: + - items: + additionalProperties: false + properties: + vehicle_id: + description: "dtype: int32, vehicle_id >= 0. \n\n\ + \ Vehicle id as an integer denoting the vehicle\ + \ index for which the break is added" + title: Vehicle Id + type: integer + earliest: + description: "dtype: int32, earliest >= 0. \n\n Earliest\ + \ break time" + title: Earliest + type: integer + latest: + description: "dtype: int32, latest > 0. \n\n Latest\ + \ break time" + title: Latest + type: integer + duration: + description: "dtype: int32, duration >= 0. \n\n Duration\ + \ of the break time" + title: Duration + type: integer + locations: + anyOf: + - items: + type: integer + type: array + - type: 'null' + description: "dtype: int32, location_id >= 0. \n\n\ + \ Location ids where this break can be taken." + title: Locations + required: + - vehicle_id + - earliest + - latest + - duration + - locations + title: VehicleBreak + type: object + type: array + - type: 'null' + description: A list of Vehicle Breaks where vehicle id can + take a break between earliest and latest time for specified + duration in the specified locations. By default any location + can be used. + examples: + - - duration: 2 + earliest: 0 + latest: 10 + locations: + - 2 + vehicle_id: 0 + - duration: 3 + earliest: 10 + latest: 15 + locations: + - 3 + - 5 + vehicle_id: 1 + - duration: 2 + earliest: 0 + latest: 5 + vehicle_id: 1 + title: Vehicle Breaks + vehicle_types: + anyOf: + - items: + type: integer + type: array + - type: 'null' + description: "dtype: uint8. \n\n Types of vehicles in the\ + \ fleet given as positive integers." + examples: + - - 1 + - 2 + title: Vehicle Types + vehicle_order_match: + anyOf: + - items: + additionalProperties: false + properties: + vehicle_id: + description: "dtype: int32, vehicle_id >= 0. \n\n\ + \ Vehicle id as an integer, and can serve all the\ + \ order listed in order_ids." + title: Vehicle Id + type: integer + order_ids: + description: "dtype: int32, order_id >= 0. \n\n Indices\ + \ of orders which can be served by this particular\ + \ vehicle" + items: + type: integer + title: Order Ids + type: array + required: + - vehicle_id + - order_ids + title: VehicleOrderMatch + type: object + type: array + - type: 'null' + description: A list of vehicle order match, where the match + would contain a vehicle id and a list of orders that vehicle + can serve. + examples: + - - order_ids: + - 0 + vehicle_id: 0 + - order_ids: + - 1 + vehicle_id: 1 + title: Vehicle Order Match + skip_first_trips: + anyOf: + - items: + type: boolean + type: array + - type: 'null' + description: Drop the cost of trip to first location for that + vehicle. + examples: + - - true + - false + title: Skip First Trips + drop_return_trips: + anyOf: + - items: + type: boolean + type: array + - type: 'null' + description: Drop cost of return trip for each vehicle. + examples: + - - true + - false + title: Drop Return Trips + min_vehicles: + anyOf: + - type: integer + - type: 'null' + description: "dtype: int32, min_vehicles >= 1. \n\n Solution\ + \ should consider minimum number of vehicles" + examples: + - 2 + title: Min Vehicles + vehicle_max_costs: + anyOf: + - items: + type: number + type: array + - type: 'null' + description: "dtype: float32, max_costs >= 0. \n\n Maximum\ + \ cost a vehicle can incur and it is based on cost matrix/cost\ + \ waypoint graph." + examples: + - - 7 + - 10 + title: Vehicle Max Costs + vehicle_max_times: + anyOf: + - items: + type: number + type: array + - type: 'null' + description: "dtype: float32, max_time >= 0. \n\n Maximum\ + \ time a vehicle can operate (includes drive, service and\ + \ wait time), this is based on travel time matrix/travel\ + \ time waypoint graph." + examples: + - - 7 + - 10 + title: Vehicle Max Times + vehicle_fixed_costs: + anyOf: + - items: + type: number + type: array + - type: 'null' + description: "dtype: float32, fixed_cost >= 0. \n\n Cost of\ + \ each vehicle.This helps in routing where may be 2 vehicles\ + \ with less cost is effective compared to 1 vehicle with\ + \ huge cost. As example shows veh-0 (15) > veh-1 (5) + veh-2\ + \ (5)" + examples: + - - 15 + - 5 + title: Vehicle Fixed Costs + required: + - vehicle_locations + title: FleetData + type: object + task_data: + additionalProperties: false + properties: + task_locations: + description: "dtype: int32, location >= 0. \n\n Location where\ + \ the task has been requested." + examples: + - - 1 + - 2 + items: + type: integer + title: Task Locations + type: array + task_ids: + anyOf: + - items: + type: string + type: array + - type: 'null' + description: List of the task ids or names provided as a string. + examples: + - - Task-A + - Task-B + title: Task Ids + demand: + anyOf: + - items: + items: + type: integer + type: array + type: array + - type: 'null' + description: "dtype: int32 \n\n Note: For this release number\ + \ of demand dimensions are limited to 3. \n\n Lists of demands\ + \ of each tasks.\nMultiple demands can be added and each\ + \ list represents one kind of demand. Order of these demands\ + \ should match the type of vehicle capacities provided.Example:\ + \ In case of two sets of demands per vehicle with 3 vehicles,\ + \ \n\n [ \n\n [dem_1_tsk_1, dem_1_tsk_2, dem_1_tsk_3],\ + \ \n\n [dem_2_tsk_1, dem_2_tsk_2, dem_2_tsk_3] \n\ + \n ]" + examples: + - - - 1 + - 1 + - - 3 + - 1 + title: Demand + pickup_and_delivery_pairs: + anyOf: + - items: + items: + type: integer + type: array + type: array + - type: 'null' + description: "dtype: int32, pairs >= 0. \n\n List of Pick-up\ + \ and delivery index pairs from task locations.\nIn case\ + \ we have the following pick-up and delivery locations,\ + \ 2->1, 4->5, 3->4, then task locations would look something\ + \ like, task_locations = [0, 2, 1, 4, 5, 3, 4] and pick-up\ + \ and delivery pairs would be index of those locations in\ + \ task location and would look like [[1, 2], [3, 4], [5,\ + \ 6]], 1 is pickup index for location 2 and it should be\ + \ delivered to location 1 which is at index 2.Example schema:\ + \ \n\n [ \n\n [pcikup_1_idx_to_task, drop_1_idx_to_task],\ + \ \n\n [pcikup_2_idx_to_task, drop_2_idx_to_task],\ + \ \n\n ]" + examples: + - null + title: Pickup And Delivery Pairs + task_time_windows: + anyOf: + - items: + items: + type: integer + type: array + type: array + - type: 'null' + description: "dtype: int32, time >= 0. \n\n Earliest and Latest\ + \ time windows for each tasks.\nFor example the data would\ + \ look as follows, \n \n\n [ \n\n [tsk_1_earliest,\ + \ tsk_1_latest], \n\n [tsk_2_earliest, tsk_2_latest]\ + \ \n\n ]" + examples: + - - - 0 + - 5 + - - 3 + - 9 + title: Task Time Windows + service_times: + anyOf: + - items: + type: integer + type: array + - additionalProperties: + items: + type: integer + type: array + type: object + - type: 'null' + description: "dtype: int32, time >= 0. \n\n Service time for\ + \ each task. Accepts a list of service times for all vehicles.\ + \ In case of vehicle specific service times, accepts a dict\ + \ with key as vehicle id and value as list of service times.Example\ + \ schema: In case all vehicles have same service times,\ + \ \n\n [tsk_1_srv_time, tsk_2_srv_time, tsk_3_srv_time]\ + \ \n\n \n\n In case, there are 2 vehicles and each of them\ + \ have different service times, \n\n { \n\n \ + \ vehicle-id-1: [tsk_1_veh_1_srv_time, tsk_2_veh_1_srv_time,\ + \ tsk_3_veh_1_srv_time], \n\n vehicle-id-2: [tsk_1_veh_2_srv_time,\ + \ tsk_2_veh_2_srv_time, tsk_3_veh_2_srv_time], \n\n \ + \ }" + examples: + - - 0 + - 0 + title: Service Times + prizes: + anyOf: + - items: + type: number + type: array + - type: 'null' + description: "dtype: float32, prizes >= 0. \n\n List of values\ + \ which signifies prizes that are collected for fulfilling\ + \ each task. This can be used effectively in case solution\ + \ is infeasible and need to drop few tasks to get feasible\ + \ solution. Solver will prioritize for higher prize tasks " + examples: + - null + title: Prizes + order_vehicle_match: + anyOf: + - items: + additionalProperties: false + properties: + order_id: + description: "dtype: int32, order_id >= 0. \n\n Indices\ + \ of orders which can be served by this particular\ + \ vehicleOrder id as an integer" + title: Order Id + type: integer + vehicle_ids: + description: "dtype: int32, vehicle_id >= 0. \n\n\ + \ Indices of the vehicles which can serve this particular\ + \ order. \n" + items: + type: integer + title: Vehicle Ids + type: array + required: + - order_id + - vehicle_ids + title: OrderVehicleMatch + type: object + type: array + - type: 'null' + description: A list of order vehicle match, where the match + would contain a order id and a list of vehicle ids that + can serve this order. + examples: + - - order_id: 0 + vehicle_ids: + - 0 + - order_id: 1 + vehicle_ids: + - 1 + title: Order Vehicle Match + required: + - task_locations + title: TaskData + type: object + initial_solution: + anyOf: + - items: + additionalProperties: + properties: + task_id: + default: [] + description: task_ids being assigned to vehicle along + with depot and breaks + items: + type: string + title: Task Id + type: array + type: + default: [] + description: "Type of routing point, whether it is Depot,\ + \ Waypoint - w \nDelivery, Break, Pickup \n" + items: + enum: + - Depot + - Pickup + - Delivery + - Break + - w + title: LocationTypeEnum + type: string + title: Type + type: array + title: VehicleSolData + type: object + default: {} + description: Details of initial solution routes + examples: + - veh-1: + task_id: + - Break + - Task-A + type: + - Break + - Delivery + veh-2: + task_id: + - Depot + - Break + - Task-B + - Depot + type: + - Depot + - Break + - Delivery + - Depot + title: InitialSolution + type: object + type: array + - type: 'null' + title: Initial Solution + solver_config: + anyOf: + - additionalProperties: false + properties: + time_limit: + anyOf: + - type: number + - type: 'null' + description: SolverSettings time limit + examples: + - 5.0 + title: Time Limit + objectives: + anyOf: + - additionalProperties: false + properties: + cost: + anyOf: + - type: number + - type: 'null' + description: "dtype: float32. \n\n The weight assigned\ + \ to minimizing the cost for a given solution, default\ + \ value is 1" + examples: + - 1 + title: Cost + travel_time: + anyOf: + - type: number + - type: 'null' + description: "dtype: float32. \n\n The weight assigned\ + \ to minimizing total travel time for a given solution\ + \ (includes drive, service and wait time)" + examples: + - 0 + title: Travel Time + variance_route_size: + anyOf: + - type: number + - type: 'null' + description: "dtype: float32. \n\n The weight assigned\ + \ to the variance in the number of orders served\ + \ by each route." + examples: + - 0 + title: Variance Route Size + variance_route_service_time: + anyOf: + - type: number + - type: 'null' + description: "dtype: float32. \n\n The weight assigned\ + \ to the variance in the accumulated service times\ + \ of each route" + examples: + - 0 + title: Variance Route Service Time + prize: + anyOf: + - type: number + - type: 'null' + description: "dtype: float32. \n\n The weight assigned\ + \ to the prize in accumulated prizes for each job\ + \ fulfilled.This will be negated from overall values\ + \ accumulated with other objectives.For example,\ + \ if cost accumulated is 10 and objective value\ + \ for it is 1, and if the prize accumulated is 3\ + \ and objective is 2, then total cost would look\ + \ something like this 10 x 1 - 3 x 2 = 4.Note: When\ + \ this value is zero the prize objective is ignored." + examples: + - 0 + title: Prize + vehicle_fixed_cost: + anyOf: + - type: number + - type: 'null' + description: "dtype: float32. \n\n The weight assigned\ + \ to the accumulated fixed costs of each vehicle\ + \ used in solution" + examples: + - 0 + title: Vehicle Fixed Cost + title: Objective + type: object + - type: 'null' + description: Values provided dictate the linear combination + of factors used to evaluate solution quality.Only prize + will be negated, all others gets accumulated. That's why + sometime you might come across negative value as solution + cost. + config_file: + anyOf: + - type: string + - type: 'null' + description: Dump configuration information in a given file + as yaml + examples: + - null + title: Config File + verbose_mode: + anyOf: + - type: boolean + - type: 'null' + default: false + description: Displaying internal information during the + solver execution. + examples: + - false + title: Verbose Mode + error_logging: + anyOf: + - type: boolean + - type: 'null' + default: true + description: Displaying constraint error information during + the solver execution. + examples: + - true + title: Error Logging + title: SolverSettingsConfig + type: object + - type: 'null' + required: + - fleet_data + - task_data + title: OptimizedRoutingData + type: object + - additionalProperties: false + properties: + csr_constraint_matrix: + additionalProperties: false + description: 'Constraints depicted in CSR format. + + For example lets consider following problem, + + + # Constraints + + + 3x + 4y <= 5.4 + + 2.7x + 10.1y <= 4.9 + + + # variables + + x, y >= 0 + + + # Objective + + minimize(0.2x + 0.1y) + + + The constraints on the top are converted to CSR and their + + indices, offsets and values are shared in this matrix. + + + Offsets depict number of coefficients/values for each constraint. + + Indices signify variable with which the coefficients/values + are associated. + + And coefficients 3, 4, 2.7, 10.1 become values. + + + For more details please refer here + # noqa' + properties: + indices: + description: Indices of constraint matrix + items: + type: integer + title: Indices + type: array + offsets: + description: Offsets of constraint matrix. + items: + type: integer + title: Offsets + type: array + values: + description: Values in constraint matrix + items: + type: number + title: Values + type: array + title: CSRConstraintMatrix + type: object + objective_data: + anyOf: + - additionalProperties: false + description: 'Coefficients of the objective data is passed as + a list, + + For example lets consider following problem, + + + # Constraints + + + 3x + 4y <= 5.4 + + 2.7x + 10.1y <= 4.9 + + + # variables + + x, y >= 0 + + + # Objective + + minimize(0.2x + 0.1y) + + + Coefficients of the objective 0.2 and 0.1 is passed in the + same order.' + properties: + coefficients: + description: Objective coefficients (c) array + items: + type: number + title: Coefficients + type: array + scalability_factor: + anyOf: + - type: number + - type: 'null' + description: Scaling factor of the objective function + title: Scalability Factor + offset: + anyOf: + - type: number + - type: 'null' + description: Offset of the objective function + title: Offset + title: ObjectiveData + type: object + - type: 'null' + default: {} + description: ' + + Coefficients of the objective data is passed as a list, + + For example lets consider following problem, + + + # Constraints + + + 3x + 4y <= 5.4 + + 2.7x + 10.1y <= 4.9 + + + # variables + + x, y >= 0 + + + # Objective + + minimize(0.2x + 0.1y) + + + Coefficients of the objective 0.2 and 0.1 is passed in the same + order. + + ' + examples: + - coefficients: + - 0.2 + - 0.1 + offset: 0.0 + scalability_factor: 1.0 + constraint_bounds: + anyOf: + - additionalProperties: false + description: 'Bounds for the constraints will be set here, + + For example lets consider following problem, + + + # Constraints + + + 3x + 4y <= 5.4 # Constraint-1 + + 2.7x + 10.1y <= 4.9 # Constraint-2 + + + # variables + + x, y >= 0 + + + # Objective + + minimize(0.2x + 0.1y) + + + Two ways to set this, + + Option #1: using upper and lower bounds, + + (Higher Priority in case both ways are used). + + + Bounds for the constraints are added here as a list, + + + Constraint-1 upper bound is 5.4 + + Constraint-2 upper bound is 4.9 + + + Since no lower bound is explicitly set, + + + Constraint-1 lower bound is negative infinity + + Constraint-2 lower bound is negative infinity + + + Option #2: Set using bounds and types + + + Set values 5.4, 4.9 as list + + + set types as "L", "L" which means Less than of equal to.' + properties: + bounds: + anyOf: + - items: + anyOf: + - type: number + - enum: + - inf + - ninf + type: string + type: array + - type: 'null' + description: Constraint bounds (b / right-hand side) array + title: Bounds + upper_bounds: + anyOf: + - items: + anyOf: + - type: number + - enum: + - inf + - ninf + type: string + type: array + - type: 'null' + description: Constraints upper bounds + title: Upper Bounds + lower_bounds: + anyOf: + - items: + anyOf: + - type: number + - enum: + - inf + - ninf + type: string + type: array + - type: 'null' + description: Constraints lower bounds + title: Lower Bounds + types: + anyOf: + - {} + - type: 'null' + description: 'Type of each row (constraint).Supported options + are,''E'' for equality ( = ): lower & upper constrains + bound equal to b''L'' for less-than ( <= ): lower constrains + bound equal to -infinity,upper constrains bound equal + to b''G'' for greater-than ( >= ): lower constrains bound + equal to b,upper constrains bound equal to +infinity' + items: + type: string + title: Types + type: array + title: ConstraintBounds + type: object + - type: 'null' + default: {} + description: ' + + Bounds for the constraints will be set here, + + For example lets consider following problem, + + + # Constraints + + + 3x + 4y <= 5.4 # Constraint-1 + + 2.7x + 10.1y <= 4.9 # Constraint-2 + + + # variables + + x, y >= 0 + + + # Objective + + minimize(0.2x + 0.1y) + + + Two ways to set this, + + Option #1: using upper and lower bounds, + + (Higher Priority in case both ways are used). + + + Bounds for the constraints are added here as a list, + + + Constraint-1 upper bound is 5.4 + + Constraint-2 upper bound is 4.9 + + + Since no lower bound is explicitly set, + + + Constraint-1 lower bound is negative infinity + + Constraint-2 lower bound is negative infinity + + + Option #2: Set using bounds and types + + + Set values 5.4, 4.9 as list + + + set types as "L", "L" which means Less than of equal to. + + + ' + examples: + - lower_bounds: + - ninf + - ninf + upper_bounds: + - 5.4 + - 4.9 + variable_bounds: + anyOf: + - additionalProperties: false + description: 'Bounds for the variable will be set here, + + For example lets consider following problem, + + + # Constraints + + + 3x + 4y <= 5.4 # Constraint-1 + + 2.7x + 10.1y <= 4.9 # Constraint-2 + + + # variables + + x, y >= 0 + + + # Objective + + minimize(0.2x + 0.1y) + + + # Since there is not specific upper bound is set for variables + + x - upper bound is infinity + + y - upper bound is infinity + + + x - lower bound is 0 + + y - lower bound is 0' + properties: + upper_bounds: + anyOf: + - items: + anyOf: + - type: number + - enum: + - inf + - ninf + type: string + type: array + - type: 'null' + description: Variables (x) upper bounds + title: Upper Bounds + lower_bounds: + anyOf: + - items: + anyOf: + - type: number + - enum: + - inf + - ninf + type: string + type: array + - type: 'null' + description: Variables (x) lower bounds + title: Lower Bounds + title: VariableBounds + type: object + - type: 'null' + default: {} + description: ' + + Bounds for the variable will be set here, + + For example lets consider following problem, + + + # Constraints + + + 3x + 4y <= 5.4 # Constraint-1 + + 2.7x + 10.1y <= 4.9 # Constraint-2 + + + # variables + + x, y >= 0 + + + # Objective + + minimize(0.2x + 0.1y) + + + # Since there is not specific upper bound is set for variables + + x - upper bound is infinity + + y - upper bound is infinity + + + x - lower bound is 0 + + y - lower bound is 0 + + ' + examples: + - lower_bounds: + - 0.0 + - 0.0 + upper_bounds: + - inf + - inf + initial_solution: + anyOf: + - additionalProperties: false + description: Initial solution for the solver. + properties: + primal: + anyOf: + - {} + - type: 'null' + description: Initial primal solution + items: + type: number + title: Primal + type: array + dual: + anyOf: + - {} + - type: 'null' + description: 'Initial dual solution
Note: Not supported + for MILP. ' + items: + type: number + title: Dual + type: array + title: InitialSolution + type: object + - type: 'null' + default: {} + description: ' ' + maximize: + anyOf: + - type: boolean + - type: 'null' + default: false + description: If set to True, solver tries to maximize objective + function else it will try to minimize + examples: + - false + title: Maximize + variable_types: + anyOf: + - {} + - type: 'null' + description: Type of each variable, this is must for MILP,Available + options are, 'I' - Integer'C' - Continuous + items: + type: string + title: Variable Types + type: array + variable_names: + anyOf: + - {} + - type: 'null' + description: Name of variables + items: + type: string + title: Variable Names + type: array + solver_config: + anyOf: + - properties: + tolerances: + anyOf: + - properties: + optimality: + description: absolute and relative tolerance on the + primal feasibility, dual feasibility, and gap + title: Optimality + type: number + absolute_primal_tolerance: + description: Absolute primal tolerance + title: Absolute Primal Tolerance + type: number + absolute_dual_tolerance: + description: 'Absolute dual tolerance NOTE: Only applicable + to LP' + title: Absolute Dual Tolerance + type: number + absolute_gap_tolerance: + description: 'Absolute gap tolerance NOTE: Only applicable + to LP' + title: Absolute Gap Tolerance + type: number + relative_primal_tolerance: + description: Relative primal tolerance + title: Relative Primal Tolerance + type: number + relative_dual_tolerance: + description: 'Relative dual tolerance NOTE: Only applicable + to LP' + title: Relative Dual Tolerance + type: number + relative_gap_tolerance: + description: 'Relative gap tolerance NOTE: Only applicable + to LP' + title: Relative Gap Tolerance + type: number + primal_infeasible_tolerance: + description: 'Primal infeasible tolerance NOTE: Only + applicable to LP' + title: Primal Infeasible Tolerance + type: number + dual_infeasible_tolerance: + description: 'Dual infeasible tolerance NOTE: Only + applicable to LP' + title: Dual Infeasible Tolerance + type: number + mip_integrality_tolerance: + description: 'NOTE: Only applicable to MILP. Integrality + tolerance.' + title: Mip Integrality Tolerance + type: number + mip_absolute_gap: + description: 'MIP gap absolute tolerance NOTE: Only + applicable to MILP' + title: Mip Absolute Gap + type: number + mip_relative_gap: + description: 'MIP gap relative tolerance NOTE: Only + applicable to MILP' + title: Mip Relative Gap + type: number + mip_absolute_tolerance: + description: MIP absolute tolerance + title: Mip Absolute Tolerance + type: number + mip_relative_tolerance: + description: MIP relative tolerance + title: Mip Relative Tolerance + type: number + title: Tolerances + type: object + - type: 'null' + default: {} + description: 'Note: Not supported for MILP.absolute is fixed + to 1e-4, relative is fixed for 1e-6 and integrality is + fixed for 1e-4.' + infeasibility_detection: + anyOf: + - type: boolean + - type: 'null' + default: false + description: ' Detect and leave if the problem is detected + as infeasible.
Note: Not supported for MILP. ' + examples: + - true + title: Infeasibility Detection + time_limit: + anyOf: + - type: number + - type: 'null' + description: 'Time limit in seconds after which the solver + will return the current solution. Mandatory in case of + MILP.
LP: Solver runs until optimality is reached + within the time limit. If it does, it will return and + will not wait for the entire duration of the time limit.
MILP: + Solver runs the entire duration of the time limit to search + for a better solution.' + examples: + - 10 + title: Time Limit + iteration_limit: + anyOf: + - type: integer + - type: 'null' + description: 'Iteration limit after which the solver will + return the current solution
Note: Not supported for + MILP. ' + title: Iteration Limit + pdlp_solver_mode: + anyOf: + - type: integer + - type: 'null' + default: 4 + description: 'Solver mode to use for PDLP:
- Stable1: + 0, Legacy stable mode
- Stable2: 1, Legacy stable mode
- + Methodical1: 2, Takes slower individual steps, but fewer + are needed to converge
- Fast1: 3, Fastest mode, but + with less success in convergence
- Stable3: 4, Best + overall mode from experiments; balances speed and convergence + success
Note: Not supported for MILP. ' + title: Pdlp Solver Mode + method: + anyOf: + - type: integer + - type: 'null' + default: 0 + description: 'Method to use:
- Concurrent: 0, Concurrent + method
- PDLP: 1, PDLP method
- Dual Simplex: 2, + Dual Simplex method
- Barrier: 3, Barrier method
Note: + Not supported for MILP. ' + title: Method + mip_scaling: + anyOf: + - type: integer + - type: 'null' + default: 1 + description: 'MIP scaling mode:
- 0: No scaling
- + 1: Full scaling (objective + row)
- 2: Row scaling + only (no objective scaling), default' + title: Mip Scaling + mip_heuristics_only: + anyOf: + - type: boolean + - type: 'null' + default: false + description: Set True to run heuristics only, False to run + heuristics and branch and bound for MILP + title: Mip Heuristics Only + mip_batch_pdlp_strong_branching: + anyOf: + - type: integer + - type: 'null' + default: 0 + description: 'Strong branching mode: 0 = Dual Simplex only, + 1 = cooperative work-stealing (DS + batch PDLP), 2 = batch + PDLP only.' + title: Mip Batch Pdlp Strong Branching + mip_batch_pdlp_reliability_branching: + anyOf: + - type: integer + - type: 'null' + default: 0 + description: 'Reliability branching mode: 0 = Dual Simplex + only, 1 = cooperative work-stealing (DS + batch PDLP), + 2 = batch PDLP only.' + title: Mip Batch Pdlp Reliability Branching + num_cpu_threads: + anyOf: + - type: integer + - type: 'null' + description: Set the number of CPU threads to use in the + MIP solver + title: Num Cpu Threads + num_gpus: + anyOf: + - type: integer + - type: 'null' + description: Set the number of GPUs to use for LP solve. + title: Num Gpus + augmented: + anyOf: + - type: integer + - type: 'null' + default: -1 + description: Set the types of system solved by the barrier + solver. -1 for automatic, 0 for ADAT, 1 for augmented + system + title: Augmented + folding: + anyOf: + - type: integer + - type: 'null' + default: -1 + description: Set if folding should be used on a linear program. + -1 for automatic, 0 to not fold, 1 to force folding + title: Folding + dualize: + anyOf: + - type: integer + - type: 'null' + default: -1 + description: Set if dualization should be used on a linear + program. -1 for automatic, 0 to turn off dualization, + 1 to force dualization + title: Dualize + ordering: + anyOf: + - type: integer + - type: 'null' + default: -1 + description: Set the type of ordering to use for the barrier + solver.-1 for automatic, 0 to use cuDSS default ordering, + 1 to use AMD + title: Ordering + barrier_dual_initial_point: + anyOf: + - type: integer + - type: 'null' + default: -1 + description: Set the type of dual initial point to use for + the barriersolver. -1 for automatic, 0 to use Lustig, + Marsten, and Shannoinitial point, 1 to use initial point + from a dual least squaresproblem + title: Barrier Dual Initial Point + eliminate_dense_columns: + anyOf: + - type: boolean + - type: 'null' + default: true + description: Set if dense columns should be eliminated from + the constraint matrix in the barrier solver. True to eliminate, + False to not eliminate + title: Eliminate Dense Columns + cudss_deterministic: + anyOf: + - type: boolean + - type: 'null' + default: false + description: Set if cuDSS should use deterministic mode. + True to use deterministic mode, False to not use deterministic + mode + title: Cudss Deterministic + crossover: + anyOf: + - type: boolean + - type: 'null' + default: false + description: Set True to use crossover, False to not use + crossover. + title: Crossover + presolve: + anyOf: + - type: integer + - type: 'null' + description: 'Set presolve mode: 0 to disable presolve, + 1 for Papilo presolve for MIP or LPs, 2 for PSLP LP presolve. + Presolve can reduce problem size and improve solve time. + Default is 1 for MIP problems and 2 for LP problems.' + title: Presolve + mip_probing: + anyOf: + - type: boolean + - type: 'null' + description: Enable or disable the cuOpt-internal probing-cache + step of MIP presolve. True (default) runs probing as part + of presolve; False skips probing while leaving the rest + of presolve untouched. Has no effect if presolve is disabled + (presolve=0) or when running in deterministic mode (probing + is already skipped). LP-only solves ignore this setting. + title: Mip Probing + dual_postsolve: + anyOf: + - type: boolean + - type: 'null' + description: Set True to enable dual postsolve, False to + disable dual postsolve. Dual postsolve can improve solve + time at the expense of not having access to the dual solution. + Default is True for LP problems when presolve is enabled. + This is not relevant for MIP problems. + title: Dual Postsolve + log_to_console: + anyOf: + - type: boolean + - type: 'null' + default: true + description: Set True to write logs to console, False to + not write logs to console. + title: Log To Console + strict_infeasibility: + anyOf: + - type: boolean + - type: 'null' + default: false + description: ' controls the strict infeasibility mode in + PDLP. When true if either the current or the average solution + is detected as infeasible, PDLP will stop. When false + both the current and average solution need to be detected + as infeasible for PDLP to stop.' + title: Strict Infeasibility + user_problem_file: + anyOf: + - type: string + - type: 'null' + default: '' + description: Ignored by the service but included for dataset + compatibility + title: User Problem File + per_constraint_residual: + anyOf: + - type: boolean + - type: 'null' + default: false + description: Controls whether PDLP should compute the primal + & dual residual per constraint instead of globally. + title: Per Constraint Residual + save_best_primal_so_far: + anyOf: + - type: boolean + - type: 'null' + default: false + description: controls whether PDLP should save the best + primal solution so far. With this parameter set to true, + PDLP will always prioritize a primal feasible to a non + primal feasible. If a new primal feasible is found, the + one with the best primal objective will be kept. If no + primal feasible was found, the one with the lowest primal + residual will be kept. If two have the same primal residual, + the one with the best objective will be kept. + title: Save Best Primal So Far + first_primal_feasible: + anyOf: + - type: boolean + - type: 'null' + default: false + description: Controls whether PDLP should stop when the + first primal feasible solution is found. + title: First Primal Feasible + log_file: + anyOf: + - type: string + - type: 'null' + default: '' + description: Ignored by the service but included for dataset + compatibility + title: Log File + solution_file: + anyOf: + - type: string + - type: 'null' + default: '' + description: Ignored by the service but included for dataset + compatibility + title: Solution File + title: SolverConfig + type: object + - type: 'null' + default: + tolerances: {} + infeasibility_detection: false + pdlp_solver_mode: 4 + method: 0 + mip_scaling: 1 + mip_heuristics_only: false + mip_batch_pdlp_strong_branching: 0 + mip_batch_pdlp_reliability_branching: 0 + augmented: -1 + folding: -1 + dualize: -1 + ordering: -1 + barrier_dual_initial_point: -1 + eliminate_dense_columns: true + cudss_deterministic: false + crossover: false + log_to_console: true + strict_infeasibility: false + user_problem_file: '' + per_constraint_residual: false + save_best_primal_so_far: false + first_primal_feasible: false + log_file: '' + solution_file: '' + description: ' ' + examples: + - tolerances: + optimality: 0.0001 + title: LPData + type: object + examples: + VRP request: + value: + cost_matrix_data: + data: + '1': + - - 0 + - 1 + - 1 + - - 1 + - 0 + - 1 + - - 1 + - 1 + - 0 + '2': + - - 0 + - 1 + - 1 + - - 1 + - 0 + - 1 + - - 1 + - 2 + - 0 + travel_time_matrix_data: + data: + '1': + - - 0 + - 1 + - 1 + - - 1 + - 0 + - 1 + - - 1 + - 1 + - 0 + '2': + - - 0 + - 1 + - 1 + - - 1 + - 0 + - 1 + - - 1 + - 2 + - 0 + fleet_data: + vehicle_locations: + - - 0 + - 0 + - - 0 + - 0 + vehicle_ids: + - veh-1 + - veh-2 + capacities: + - - 2 + - 2 + - - 4 + - 1 + vehicle_time_windows: + - - 0 + - 10 + - - 0 + - 10 + vehicle_break_time_windows: + - - - 1 + - 2 + - - 2 + - 3 + vehicle_break_durations: + - - 1 + - 1 + vehicle_break_locations: + - 0 + - 1 + vehicle_types: + - 1 + - 2 + vehicle_order_match: + - order_ids: + - 0 + vehicle_id: 0 + - order_ids: + - 1 + vehicle_id: 1 + skip_first_trips: + - true + - false + drop_return_trips: + - true + - false + min_vehicles: 2 + vehicle_max_costs: + - 7 + - 10 + vehicle_max_times: + - 7 + - 10 + vehicle_fixed_costs: + - 15 + - 5 + task_data: + task_locations: + - 1 + - 2 + task_ids: + - Task-A + - Task-B + demand: + - - 1 + - 1 + - - 3 + - 1 + task_time_windows: + - - 0 + - 5 + - - 3 + - 9 + service_times: + - 0 + - 0 + order_vehicle_match: + - order_id: 0 + vehicle_ids: + - 0 + - order_id: 1 + vehicle_ids: + - 1 + solver_config: + time_limit: 1 + objectives: + cost: 1 + travel_time: 0 + variance_route_size: 0 + variance_route_service_time: 0 + prize: 0 + vehicle_fixed_cost: 0 + verbose_mode: false + error_logging: true + LP request: + value: + csr_constraint_matrix: + offsets: + - 0 + - 2 + - 4 + indices: + - 0 + - 1 + - 0 + - 1 + values: + - 3.0 + - 4.0 + - 2.7 + - 10.1 + constraint_bounds: + upper_bounds: + - 5.4 + - 4.9 + lower_bounds: + - ninf + - ninf + objective_data: + coefficients: + - 0.2 + - 0.1 + scalability_factor: 1.0 + offset: 0.0 + variable_bounds: + upper_bounds: + - inf + - inf + lower_bounds: + - 0.0 + - 0.0 + maximize: false + solver_config: + tolerances: + optimality: 0.0001 + application/vnd.msgpack: + schema: + type: string + format: byte + examples: + VRP request compressed with msgpack: + value: \x85\xb0cost_matrix_data\x81\xa4data\x82\xa11\x93\x93\x00\x01\x01\x93\x01\x00\x01\x93\x01\x01\x00\xa12\x93\x93\x00\x01\x01\x93\x01\x00\x01\x93\x01\x02\x00\xb7travel_time_matrix_data\x81\xa4data\x82\xa11\x93\x93\x00\x01\x01\x93\x01\x00\x01\x93\x01\x01\x00\xa12\x93\x93\x00\x01\x01\x93\x01\x00\x01\x93\x01\x02\x00\xaafleet_data\x8f\xb1vehicle_locations\x92\x92\x00\x00\x92\x00\x00\xabvehicle_ids\x92\xa5veh-1\xa5veh-2\xaacapacities\x92\x92\x02\x02\x92\x04\x01\xb4vehicle_time_windows\x92\x92\x00\n\x92\x00\n\xbavehicle_break_time_windows\x91\x92\x92\x01\x02\x92\x02\x03\xb7vehicle_break_durations\x91\x92\x01\x01\xb7vehicle_break_locations\x92\x00\x01\xadvehicle_types\x92\x01\x02\xb3vehicle_order_match\x92\x82\xa9order_ids\x91\x00\xaavehicle_id\x00\x82\xa9order_ids\x91\x01\xaavehicle_id\x01\xb0skip_first_trips\x92\xc3\xc2\xb1drop_return_trips\x92\xc3\xc2\xacmin_vehicles\x02\xb1vehicle_max_costs\x92\x07\n\xb1vehicle_max_times\x92\x07\n\xb3vehicle_fixed_costs\x92\x0f\x05\xa9task_data\x86\xaetask_locations\x92\x01\x02\xa8task_ids\x92\xa6Task-A\xa6Task-B\xa6demand\x92\x92\x01\x01\x92\x03\x01\xb1task_time_windows\x92\x92\x00\x05\x92\x03\t\xadservice_times\x92\x00\x00\xb3order_vehicle_match\x92\x82\xa8order_id\x00\xabvehicle_ids\x91\x00\x82\xa8order_id\x01\xabvehicle_ids\x91\x01\xadsolver_config\x84\xaatime_limit\x01\xaaobjectives\x86\xa4cost\x01\xabtravel_time\x00\xb3variance_route_size\x00\xbbvariance_route_service_time\x00\xa5prize\x00\xb2vehicle_fixed_cost\x00\xacverbose_mode\xc2\xaderror_logging\xc3 + LP request compressed with msgpack: + value: \x86\xb5csr_constraint_matrix\x83\xa7offsets\x93\x00\x02\x04\xa7indices\x94\x00\x01\x00\x01\xa6values\x94\x03\x04\xcb@\x05\x99\x99\x99\x99\x99\x9a\xcb@$333333\xb1constraint_bounds\x82\xacupper_bounds\x92\xcb@\x15\x99\x99\x99\x99\x99\x9a\xcb@\x13\x99\x99\x99\x99\x99\x9a\xaclower_bounds\x92\xa4ninf\xa4ninf\xaeobjective_data\x83\xaccoefficients\x92\xcb?\xc9\x99\x99\x99\x99\x99\x9a\xcb?\xb9\x99\x99\x99\x99\x99\x9a\xb2scalability_factor\x01\xa6offset\x00\xafvariable_bounds\x82\xacupper_bounds\x92\xa3inf\xa3inf\xaclower_bounds\x92\x00\x00\xa8maximize\xc2\xadsolver_config\x81\xaatolerances\x81\xaaoptimality\xcb?\x1a6\xe2\xeb\x1cC- + application/zlib: + schema: + type: string + format: byte + examples: + LP request compressed with zlib: + value: x\x01\x8dR\xd1j\xc4 \x10|\xcfW\x88\xcf%$\xd7\x94\xd2\xfeJ9\xc2\xc6h\xd9b4\xa8\xc9]{\xe4\xdf\xab1\x9a\x0b\x1c\xa5o\xbb#\xb3;\xb3\xe3\xad + \x842kZ\xa6\x95u\x06P\xb9v\x00g\xf0J\xdf\xc9\xcd?\xfag-\x84\xe5\xcez\xe0c\x05\x08\xa9\x9e\xb6\xe2\x94\x8af\x05\xce\xb1\xa5\xa8zd\xfc!\xa3N\x8c<\xa3>Pg\x90\xd3\x81\xf9\x9c\x08M*N\xe5k*\xeb\xaa\xdc\xe8~\xc8\x12Pzg\xa4\xd3\x93\xea\x83\x8a\xcd\xc84\x8e\xdc\xb4\x19Mn^\xca<\xba)\xdf\x0ej\xa4\xbe DataModel: def toDict(model, json=False): + """Convert a :class:`DataModel` to a plain Python dictionary. + + Serializes all problem arrays (constraint matrix, bounds, objective + coefficients, variable types, and names) into a nested ``dict``. + Infinity values (``numpy.inf`` / ``-numpy.inf``) are replaced with the + strings ``"inf"`` / ``"ninf"`` when ``json=True`` so the result is + JSON-serializable. + + Parameters + ---------- + model : DataModel + A problem loaded via :func:`Read` or :func:`ParseMps`. + json : bool + If ``True``, converts NumPy arrays to Python lists and replaces + infinity values with ``"inf"`` / ``"ninf"`` strings so the result + can be passed to :func:`json.dumps`. Defaults to ``False``. + + Returns + ------- + dict + Nested dictionary with keys: ``csr_constraint_matrix``, + ``constraint_bounds``, ``objective_data``, ``variable_bounds``, + ``maximize``, ``variable_types``, ``variable_names``. + + Raises + ------ + ValueError + If *model* is not a :class:`DataModel` instance. + """ if not isinstance(model, parser_wrapper.DataModel): raise ValueError( "model must be a cuopt.linear_programming.io.parser_wrapper.DataModel" diff --git a/python/cuopt/cuopt/linear_programming/problem.py b/python/cuopt/cuopt/linear_programming/problem.py index 10600a543b..ff0e10aceb 100644 --- a/python/cuopt/cuopt/linear_programming/problem.py +++ b/python/cuopt/cuopt/linear_programming/problem.py @@ -17,12 +17,17 @@ class VType(str, Enum): - """ - The type of a variable is continuous, integer, or semi-continuous. - Variable Types can be directly used as a constant. - CONTINUOUS is VType.CONTINUOUS - INTEGER is VType.INTEGER - SEMI_CONTINUOUS is VType.SEMI_CONTINUOUS + """Type of an LP/MIP variable. + + Attributes + ---------- + CONTINUOUS : str + Continuous variable (default). Equivalent to the module-level constant ``CONTINUOUS``. + INTEGER : str + Integer variable. Equivalent to the module-level constant ``INTEGER``. + SEMI_CONTINUOUS : str + Semi-continuous variable: either zero or within a given range. + Equivalent to the module-level constant ``SEMI_CONTINUOUS``. """ CONTINUOUS = "C" @@ -36,12 +41,16 @@ class VType(str, Enum): class CType(str, Enum): - """ - The sense of a constraint is either LE, GE or EQ. - Constraint Sense Types can be directly used as a constant. - LE is CType.LE - GE is CType.GE - EQ is CType.EQ + """Sense of a constraint. + + Attributes + ---------- + LE : str + Less-than-or-equal (≤). Equivalent to the module-level constant ``LE``. + GE : str + Greater-than-or-equal (≥). Equivalent to the module-level constant ``GE``. + EQ : str + Equality (=). Equivalent to the module-level constant ``EQ``. """ LE = "L" @@ -55,11 +64,16 @@ class CType(str, Enum): class sense(int, Enum): - """ - The sense of a model is either MINIMIZE or MAXIMIZE. - Model objective sense can be directly used as a constant. - MINIMIZE is sense.MINIMIZE - MAXIMIZE is sense.MAXIMIZE + """Objective sense of a model. + + Attributes + ---------- + MINIMIZE : int + Minimize the objective function (default). + Equivalent to the module-level constant ``MINIMIZE``. + MAXIMIZE : int + Maximize the objective function. + Equivalent to the module-level constant ``MAXIMIZE``. """ MAXIMIZE = -1 diff --git a/python/cuopt/cuopt/linear_programming/solution/__init__.py b/python/cuopt/cuopt/linear_programming/solution/__init__.py index dfedeb991d..55a470ab82 100644 --- a/python/cuopt/cuopt/linear_programming/solution/__init__.py +++ b/python/cuopt/cuopt/linear_programming/solution/__init__.py @@ -1,4 +1,6 @@ -# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 from .solution import Solution + +__all__ = ["PDLPWarmStartData", "Solution"] diff --git a/python/cuopt/cuopt/linear_programming/solver/__init__.py b/python/cuopt/cuopt/linear_programming/solver/__init__.py index 2b2f7c21ce..437fb5adb7 100644 --- a/python/cuopt/cuopt/linear_programming/solver/__init__.py +++ b/python/cuopt/cuopt/linear_programming/solver/__init__.py @@ -1,4 +1,6 @@ -# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 from cuopt.linear_programming.solver.solver import BatchSolve, Solve + +__all__ = ["BatchSolve", "Solve"] diff --git a/python/cuopt/cuopt/routing/__init__.py b/python/cuopt/cuopt/routing/__init__.py index 081d58f998..27c43328e9 100644 --- a/python/cuopt/cuopt/routing/__init__.py +++ b/python/cuopt/cuopt/routing/__init__.py @@ -10,4 +10,21 @@ ) from cuopt.routing.utils_wrapper import DatasetDistribution from cuopt.routing.vehicle_routing import BatchSolve, DataModel, Solve, SolverSettings -from cuopt.routing.vehicle_routing_wrapper import ErrorStatus, Objective +from cuopt.routing.vehicle_routing_wrapper import ErrorStatus, NodeType, Objective + +__all__ = [ + "Assignment", + "SolutionStatus", + "BatchSolve", + "DataModel", + "DatasetDistribution", + "ErrorStatus", + "NodeType", + "Objective", + "Solve", + "SolverSettings", + "add_vehicle_constraints", + "create_pickup_delivery_data", + "generate_dataset", + "update_routes_and_vehicles", +] diff --git a/python/cuopt/cuopt/routing/assignment.py b/python/cuopt/cuopt/routing/assignment.py index 74b7409fb2..4008d6115e 100644 --- a/python/cuopt/cuopt/routing/assignment.py +++ b/python/cuopt/cuopt/routing/assignment.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 from enum import Enum @@ -7,6 +7,20 @@ class SolutionStatus(Enum): + """Status of a routing solve. + + Attributes + ---------- + SUCCESS : int + A valid solution was found. + FAIL : int + The solver failed to find a feasible solution. + TIMEOUT : int + The solver reached the time limit before finding a solution. + EMPTY : int + No orders were provided; the solution is trivially empty. + """ + SUCCESS = 0 FAIL = 1 TIMEOUT = 2 diff --git a/skills/cuopt-developer/SKILL.md b/skills/cuopt-developer/SKILL.md index 5028fb657a..e6b4f2b2cd 100644 --- a/skills/cuopt-developer/SKILL.md +++ b/skills/cuopt-developer/SKILL.md @@ -244,6 +244,7 @@ For build/test pitfalls (Cython rebuild, OOM, CUDA driver mismatch, missing `nvc - **CI scripts**: [ci/README.md](../../ci/README.md) - **Release scripts**: [ci/release/README.md](../../ci/release/README.md) - **Docs build**: [docs/cuopt/README.md](../../docs/cuopt/README.md) +- **Fern docs maintenance** (add symbols, examples, nav, pre-commit notes): [references/fern_docs.md](references/fern_docs.md) - **Python binding architecture**: [references/python_bindings.md](references/python_bindings.md) _Shell-execution, install, conda-env, and sudo policies are covered by [Refusal Rules — Read First](#refusal-rules--read-first) at the top of this skill._ diff --git a/skills/cuopt-developer/references/fern_docs.md b/skills/cuopt-developer/references/fern_docs.md new file mode 100644 index 0000000000..7572bbed34 --- /dev/null +++ b/skills/cuopt-developer/references/fern_docs.md @@ -0,0 +1,58 @@ +# Fern Docs — Developer Reference + +The cuOpt docs live in `fern/`. Content is a mix of hand-edited MDX pages and auto-generated API reference pages. `fern/README.md` has the quick-start; this file covers the patterns you need when modifying or extending the system. + +## Generated vs hand-edited pages + +| Page type | Location | Edit how | +|---|---|---| +| Python API reference | `fern/docs/pages/cuopt-python/*/api.mdx` | Edit docstrings in source; regenerate | +| C API reference | `fern/docs/pages/cuopt-c/*/api.mdx` | Edit Doxygen comments + skeleton markers; regenerate | +| Example code blocks | `*-examples.mdx` | Edit source files in `examples/`; regenerate | +| Feature/guide pages | Everything else in `docs/pages/` | Edit MDX directly | + +Run `python fern/generate_api_docs.py` after any change to source docstrings or C headers. Never hand-edit generated files. + +## Python API: adding or changing a symbol + +**Show a new symbol:** Add it to the module's `__all__` and add a NumPy-style docstring. Regenerate. + +**Control ordering:** Edit the `order` list in the relevant `PAGE_SOURCES` dir config in `extract_python_api.py`. Stems listed there are processed first; unlisted stems go alphabetically after. + +**Hide a symbol:** Remove it from `__all__`, or add its module stem to the `exclude` set in `PAGE_SOURCES`. + +**Wrapper `.pyx` files** (`*_wrapper.pyx`): only enums/classes surface — functions are always suppressed. Use the parent `.py` file for any functions you want documented. + +**Enum member descriptions:** Use a NumPy `Attributes` section in the class docstring. Plain `MEMBER = value` with no docstring renders as a bare member list. + +## C API: adding a new symbol + +1. Add a Doxygen block comment above the declaration in `cuopt_c.h` or `constants.h`. +2. Place `{/* symbol: SYMBOL_NAME */}{/* /symbol */}` in the right section of the skeleton MDX. +3. Regenerate — the content fills in automatically. + +If the script errors saying a symbol has no marker, add the marker first. + +## MDX comment markers + +Embed and symbol markers use JSX comment syntax (not HTML comments — Fern's MDX parser rejects ``): + +``` +{/* embed: examples/filename.py */} +...code block... +{/* /embed */} + +{/* symbol: cuOptSolve */} +...rendered docs... +{/* /symbol */} +``` + +## Navigation + +`docs-v26-08.yml` defines the nav tree. The Python API entries between `# BEGIN auto-generated python-api pages` and `# END auto-generated python-api pages` are rewritten automatically by `extract_python_api.py`. Everything outside those markers is hand-managed. + +## Pre-commit notes + +- `fern/openapi/cuopt_spec.yaml` and `fern/docs/scripts/cuopt-install-version.js` are auto-generated from `VERSION` and are excluded from `verify-hardcoded-version`. +- The `26.08 (Latest)` entry in `docs.yml` carries a `# rapids-pre-commit-hooks: disable-next-line` suppression. +- Run `pre-commit run --all-files` before pushing; `check-yaml` and `RAPIDS dependency file generator` failures on `dependencies.yaml` are pre-existing on `main` and unrelated to docs changes.