Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/promote_nightly_to_release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

name: Promote nightly images to release on NGC staging

on:
workflow_dispatch:
inputs:
nightly_tag_prefix:
description: "Nightly IMAGE_TAG_PREFIX to promote from (e.g. 26.8.0a)"
required: true
type: string
release_version:
description: "Release version to tag as (e.g. 26.8.0)"
required: true
type: string
cuda_ver:
description: 'JSON array of CUDA versions to promote (e.g. ["12.9.0", "13.3.0"])'
required: false
type: string
default: '["12.9.0", "13.3.0"]'
python_ver:
description: 'JSON array of Python versions to promote (e.g. ["3.14.4"])'
required: false
type: string
default: '["3.14.4"]'

defaults:
run:
shell: bash

permissions: {}

jobs:
promote-images:
name: Promote cuda${{ matrix.cuda_ver }}-py${{ matrix.python_ver }}
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
cuda_ver: ${{ fromJson(inputs.cuda_ver) }}
python_ver: ${{ fromJson(inputs.python_ver) }}
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false

- name: Login to NGC
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: "nvcr.io"
username: "$oauthtoken"
password: ${{ secrets.CUOPT_NGC_DOCKER_KEY }}

- name: Trim CUDA and Python versions
id: trim
env:
CUDA_VER: ${{ matrix.cuda_ver }}
PYTHON_VER: ${{ matrix.python_ver }}
run: |
echo "CUDA_SHORT=$(echo "$CUDA_VER" | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/')" >> $GITHUB_OUTPUT
echo "PYTHON_SHORT=$(echo "$PYTHON_VER" | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/')" >> $GITHUB_OUTPUT

- name: Promote nightly to release
env:
NIGHTLY_TAG_PREFIX: ${{ inputs.nightly_tag_prefix }}
RELEASE_VERSION: ${{ inputs.release_version }}
CUDA_SHORT: ${{ steps.trim.outputs.CUDA_SHORT }}
PYTHON_SHORT: ${{ steps.trim.outputs.PYTHON_SHORT }}
run: bash ci/docker/promote_nightly_to_release.sh
86 changes: 86 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,91 @@
# Release Notes

## Release Notes 26.08

### New Features (26.08)

- New Multi-GPU PDLP: distribute LP solves across multiple GPUs using METIS partitioning; 2.5x–8.8x speedup on 8 NVLink-connected B200 GPUs
- Exploit sparsity in Barrier's augmented system on problems with large second order cone constraints
- New free-format MPS parser with faster parse time on large models
- New recursive RINS heuristic for MIP
- New zero-half (odd-cycle) cuts for MIP
- New vector length diving and Farkas diving heuristics for MIP
- Routing: accept NumPy and pandas inputs in the routing Python `DataModel` in addition to cuDF
- C API: extend getters with additional query functions
- C API: automatic CPU/GPU memory selection for problem construction
- gRPC: Python interface to the C++ gRPC async client
- gRPC: allow TLS arguments in the Python gRPC async API
- UBI10 (Red Hat Universal Base Image) container variant for FIPS 140-3 compliant environments; image tags use `-ubi10` suffix (e.g. `latest-cu13-ubi10`)

### Breaking Changes (26.08)

- Routing `DataModel` now defers GPU device construction to solve time; applications that accessed device-side data between `DataModel` construction and the solve call must be updated

### Improvements (26.08)

- Conflict graph improvements: incorporate non-binary rows and probing implications
- Reduce latency in LP concurrent mode
- Reduce memory footprint of PDLP
- Reduce QP solver overhead
- Improve Ruiz equilibration heuristic to also check column imbalance on QPs
- Run feasibility-jump (CPU-FJ) heuristics at the root node
- Papilo-based primal/dual crush in MIP presolve
- Expose diving hyperparameters for MIP solver configuration
- Unify threading model in the MIP solver using OpenMP tasks
- MIP log cleanup and improved readability
- Add short `cu12`/`cu13` Docker tag aliases
- Remove `cuda-python` as an explicit dependency
- gRPC: `objective_scaling_factor` is now optional in requests
- gRPC: assign workers to distinct GPUs via `cudaSetDevice`

### Bug Fixes (26.08)

- Allow zero-valued coefficient updates in the LP Python model
- Fix an issue in PDLP with cublas error capture and hang on infeasible solutions
- Fix an exception in concurrent LP cleanup
- Fix bug in barrier solver where cuDSS descriptors were being freed before their backing buffers
- Fix a bug in MIP where the lower bound was incorrect when using a single thread
- Fix a bug in MIP where nodes were lost in branch and bound
- Fix a bug in MIP where root cut pass CPU feasibility-jump solutions were dropped by GPU heuristics
- Add guard in MIP for huge bounds in bounds propagation
- Fix a bug in MIP with incorrect GF2 presolve constraint addressing
- Fix a bug in MIP in variable fixing for initial solutions
- Fix issue in MIP in clique size computation and numerical issues
- Fix an issue in MIP where cut generation did not obey the time limit
- Fix an issue on QPs where maximization was not supported
- Fix rotated second-order cone detection: make canonical quadratic constraint Q matrix in triplet form
- Fix nonconvex quadratic constraint detection bug
- Fix a bug on QCQP models that incorrectly threw an unsupported exception
- Fix second-order cone index collision and incorrect quadratic constraint to cone conversion for rotated cone constraints
- Validate MPS row type byte before enum cast to avoid undefined behavior
- Fix destruction order and by-reference capture bugs in solve.cu
- Fix route priority sort indexing in routing
- Fix vehicle fixed cost accounting in fragment-vs-route deltas in routing
- Fix routing min-vehicles bug
- Fix libomp ABI incompatibility
- gRPC: fix race condition in gRPC with log streaming
- gRPC: cancel active jobs on delete
- gRPC: terminate workers cleanly on server shutdown
- gRPC: drain all remaining log lines at job completion in `StreamLogs`
- Fix row-major layout not preserved when resizing routing capacity routes
- Fix routing YAML best-results export
- Fix double `va_start` undefined behavior in C API error handling

### Documentation (26.08)

- Align cuOpt documentation branding and update Doxygen configuration
- Update cuOpt MIP positioning documentation

### New Contributors (26.08)

- @jolorunyomi
- @cafzal
- @Sylendran95
- @jackthepunished
- @fallintoplace
- @arhag23
- @divyegala

## Release Notes 26.06

### New Features (26.06)
Expand Down
107 changes: 107 additions & 0 deletions ci/docker/promote_nightly_to_release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/bin/bash

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

# Promote nightly per-arch images to release tags on NGC staging and create
# multi-arch manifests. No rebuild is performed — images are pulled, retagged,
# pushed, and stitched into manifests.
#
# Required environment variables:
# NIGHTLY_TAG_PREFIX — nightly IMAGE_TAG_PREFIX, e.g. 26.8.0a
# RELEASE_VERSION — release version to tag as, e.g. 26.8.0
# CUDA_SHORT — trimmed CUDA version, e.g. 12.9
# PYTHON_SHORT — trimmed Python version, e.g. 3.14

set -euo pipefail

REGISTRY="nvcr.io/nvstaging/nvaie/cuopt"
CUDA_MAJOR="${CUDA_SHORT%%.*}"

promote_image() {
local src=$1
local dst=$2
echo "Promoting: $src -> $dst"
docker pull "$src"
docker tag "$src" "$dst"
docker push "$dst"
echo "✓ Promoted: $dst"
}

create_manifest() {
local manifest_name=$1
local amd64_image=$2
local arm64_image=$3

echo "Creating manifest: $manifest_name"
docker manifest create --amend "$manifest_name" "$amd64_image" "$arm64_image"
docker manifest annotate "$manifest_name" "$arm64_image" --arch arm64
docker manifest annotate "$manifest_name" "$amd64_image" --arch amd64
docker manifest push "$manifest_name"
echo "✓ Pushed manifest: $manifest_name"
}

# ── cuda+py per-arch images ────────────────────────────────────────────────────

NIGHTLY_AMD64="${REGISTRY}:${NIGHTLY_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-amd64"
NIGHTLY_ARM64="${REGISTRY}:${NIGHTLY_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-arm64"
RELEASE_AMD64="${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-amd64"
RELEASE_ARM64="${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-arm64"

echo "=== Promoting cuda+py per-arch images ==="
promote_image "$NIGHTLY_AMD64" "$RELEASE_AMD64"
promote_image "$NIGHTLY_ARM64" "$RELEASE_ARM64"

# ── cuda+py and cu<major> manifests ───────────────────────────────────────────

echo "=== Creating release manifests ==="
create_manifest \
"${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}" \
"$RELEASE_AMD64" "$RELEASE_ARM64"

create_manifest \
"${REGISTRY}:${RELEASE_VERSION}-cu${CUDA_MAJOR}" \
"$RELEASE_AMD64" "$RELEASE_ARM64"

echo "=== Creating latest manifests ==="
create_manifest \
"${REGISTRY}:latest-cuda${CUDA_SHORT}-py${PYTHON_SHORT}" \
"$RELEASE_AMD64" "$RELEASE_ARM64"

create_manifest \
"${REGISTRY}:latest-cu${CUDA_MAJOR}" \
"$RELEASE_AMD64" "$RELEASE_ARM64"

# ── UBI10 (CUDA 13+ only) ─────────────────────────────────────────────────────

if [[ "${CUDA_MAJOR}" == "13" ]]; then
NIGHTLY_UBI10_AMD64="${REGISTRY}:${NIGHTLY_TAG_PREFIX}-cuda${CUDA_SHORT}-ubi10-amd64"
NIGHTLY_UBI10_ARM64="${REGISTRY}:${NIGHTLY_TAG_PREFIX}-cuda${CUDA_SHORT}-ubi10-arm64"
RELEASE_UBI10_AMD64="${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-ubi10-amd64"
RELEASE_UBI10_ARM64="${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-ubi10-arm64"

echo "=== Promoting UBI10 per-arch images ==="
promote_image "$NIGHTLY_UBI10_AMD64" "$RELEASE_UBI10_AMD64"
promote_image "$NIGHTLY_UBI10_ARM64" "$RELEASE_UBI10_ARM64"

echo "=== Creating UBI10 release manifests ==="
create_manifest \
"${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-ubi10" \
"$RELEASE_UBI10_AMD64" "$RELEASE_UBI10_ARM64"

create_manifest \
"${REGISTRY}:${RELEASE_VERSION}-cu${CUDA_MAJOR}-ubi10" \
"$RELEASE_UBI10_AMD64" "$RELEASE_UBI10_ARM64"

create_manifest \
"${REGISTRY}:latest-cuda${CUDA_SHORT}-ubi10" \
"$RELEASE_UBI10_AMD64" "$RELEASE_UBI10_ARM64"

create_manifest \
"${REGISTRY}:latest-cu${CUDA_MAJOR}-ubi10" \
"$RELEASE_UBI10_AMD64" "$RELEASE_UBI10_ARM64"
else
echo "Skipping UBI10 (CUDA_MAJOR='${CUDA_MAJOR}' — UBI10 requires CUDA 13+)"
fi

echo "=== Promotion to release complete ==="
Loading