94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
193 lines
7.0 KiB
Python
193 lines
7.0 KiB
Python
"""IPM LP Solver entry point — dispatches to the fused JIT CUDA kernel.
|
|
|
|
Solves: min c^T x subject to Ax = b, x >= 0
|
|
using a barrier (interior point) method with 5 iterations.
|
|
|
|
The fused kernel lives in ``cuda_solver`` (CUDA C++ via ``load_jit``,
|
|
backed by header-only cuBLASDx + a hand-written block Cholesky). This
|
|
module is the public-facing import surface for callers (``LPLBSolver``)
|
|
and resolves/caches the backend on first use.
|
|
|
|
LPLB requires Hopper-class hardware and Math-DX cuBLASDx headers. If
|
|
either is missing, ``warmup`` and ``solve_ipm`` raise — there is no
|
|
silent fallback.
|
|
"""
|
|
|
|
import logging
|
|
|
|
import torch
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# Backend dispatch state (resolved on first call, cached afterwards)
|
|
_BACKEND_CHECKED = False
|
|
_FUSED_AVAILABLE = False
|
|
_FUSED_SOLVE_IPM = None # type: ignore[assignment]
|
|
_FUSED_WARMUP = None # type: ignore[assignment]
|
|
_FUSED_ASSERT_FITS = None # type: ignore[assignment]
|
|
|
|
|
|
def _init_fused_backend() -> None:
|
|
"""Resolve the fused backend once. Records WHY it's disabled when it is."""
|
|
global _BACKEND_CHECKED, _FUSED_AVAILABLE
|
|
global _FUSED_SOLVE_IPM, _FUSED_WARMUP, _FUSED_ASSERT_FITS
|
|
|
|
if _BACKEND_CHECKED:
|
|
return
|
|
_BACKEND_CHECKED = True
|
|
|
|
if not torch.cuda.is_available():
|
|
logger.info("LPLB fused solver disabled: CUDA not available")
|
|
return
|
|
|
|
cap = torch.cuda.get_device_capability()
|
|
if cap[0] < 9:
|
|
logger.info(
|
|
f"LPLB fused solver disabled: GPU SM {cap[0]}.{cap[1]} < 9.0 "
|
|
"(requires Hopper or newer)"
|
|
)
|
|
return
|
|
|
|
try:
|
|
from sglang.jit_kernel.lplb.cuda_solver import solve_ipm as fused_solve_ipm
|
|
from sglang.jit_kernel.lplb.cuda_solver import warmup as fused_warmup
|
|
from sglang.jit_kernel.lplb.shmem_budget import assert_fits
|
|
except ImportError as e:
|
|
logger.info(
|
|
f"LPLB fused solver disabled: {e}. "
|
|
"Install Math-DX cuBLASDx via `pip install nvidia-mathdx` "
|
|
"or set MATHDX_HOME to an extracted archive."
|
|
)
|
|
return
|
|
|
|
_FUSED_SOLVE_IPM = fused_solve_ipm
|
|
_FUSED_WARMUP = fused_warmup
|
|
_FUSED_ASSERT_FITS = assert_fits
|
|
_FUSED_AVAILABLE = True
|
|
logger.info("LPLB fused solver enabled (CUDA C++ via load_jit, cuBLASDx)")
|
|
|
|
|
|
def _unavailable_reason() -> str:
|
|
if not torch.cuda.is_available():
|
|
return "CUDA is not available"
|
|
cap = torch.cuda.get_device_capability()
|
|
if cap[0] < 9:
|
|
return f"GPU SM {cap[0]}.{cap[1]} < 9.0 (requires Hopper or newer)"
|
|
return (
|
|
"Math-DX cuBLASDx headers not found — install via "
|
|
"`pip install nvidia-mathdx` or set MATHDX_HOME"
|
|
)
|
|
|
|
|
|
def warmup(nc: int, nv: int, num_iters: int = 5, device: str = "cuda") -> None:
|
|
"""Pre-JIT-compile the fused kernel for a given (NC, NV) shape.
|
|
|
|
Call once per unique shape at solver construction time to hide the
|
|
20-40s JIT compilation cost. Raises if the fused backend is
|
|
unavailable, the shape exceeds the shmem budget, or the kernel
|
|
fails to compile/launch.
|
|
"""
|
|
_init_fused_backend()
|
|
if not _FUSED_AVAILABLE:
|
|
raise RuntimeError(f"LPLB fused solver unavailable: {_unavailable_reason()}")
|
|
_FUSED_ASSERT_FITS(nc, nv, gpu="h100")
|
|
_FUSED_WARMUP(nc, nv, num_iters=num_iters, device=device)
|
|
|
|
|
|
def solve_ipm(
|
|
A: torch.Tensor,
|
|
b: torch.Tensor,
|
|
c: torch.Tensor,
|
|
num_iters: int = 5,
|
|
) -> torch.Tensor:
|
|
"""Barrier-method Interior Point solver for standard-form LP.
|
|
|
|
Dispatches to the JIT-compiled CUDA C++ kernel (Hopper+ GPU with
|
|
Math-DX cuBLASDx headers, reachable via ``nvidia-mathdx`` PyPI
|
|
package or ``MATHDX_HOME``). Raises if the fused backend is
|
|
unavailable or the inputs aren't on CUDA in float32.
|
|
|
|
Args:
|
|
A: Constraint matrix, shape (NC, NV), float32, on CUDA.
|
|
b: RHS vector, shape (NC,), float32, on CUDA.
|
|
c: Objective coefficients, shape (NV,), float32, on CUDA.
|
|
num_iters: Number of barrier iterations (default 5).
|
|
|
|
Returns:
|
|
x: Solution vector, shape (NV,), float32. The kernel writes 0.5
|
|
for every entry on non-convergence.
|
|
"""
|
|
nc, nv = A.shape
|
|
assert b.shape == (nc,), f"b shape mismatch: {b.shape} vs ({nc},)"
|
|
assert c.shape == (nv,), f"c shape mismatch: {c.shape} vs ({nv},)"
|
|
|
|
_init_fused_backend()
|
|
if not _FUSED_AVAILABLE:
|
|
raise RuntimeError(f"LPLB fused solver unavailable: {_unavailable_reason()}")
|
|
if not A.is_cuda:
|
|
raise RuntimeError(
|
|
f"LPLB fused solver requires CUDA tensors; got A on {A.device}."
|
|
)
|
|
if A.dtype != torch.float32:
|
|
raise RuntimeError(
|
|
f"LPLB fused solver requires float32; got A.dtype={A.dtype}."
|
|
)
|
|
return _FUSED_SOLVE_IPM(A, b, c, num_iters=num_iters)
|
|
|
|
|
|
def solve_ipm_torch_reference(
|
|
A: torch.Tensor,
|
|
b: torch.Tensor,
|
|
c: torch.Tensor,
|
|
num_iters: int = 5,
|
|
) -> torch.Tensor:
|
|
"""Pure-torch reference for the fused IPM kernel — testing only.
|
|
|
|
Mirrors the barrier-method iteration in ``csrc/lplb/ipm.cuh``
|
|
step-for-step so the two can be compared numerically:
|
|
|
|
x <- 1
|
|
for _ in range(num_iters):
|
|
ax2 = A * x^2 # (NC, NV)
|
|
ax2a = ax2 @ A^T # (NC, NC) KKT matrix
|
|
delta = solve(ax2a, ax2 @ c)
|
|
r = delta^T @ A # (NV,)
|
|
d = x * (c - r)
|
|
alpha = 0.999 / d_max (or 1.0 if d_max <= 1e-9)
|
|
x *= 1 - alpha * d
|
|
write 0.5 everywhere on non-convergence.
|
|
|
|
NOT bit-equivalent to the kernel: the kernel factors the KKT system
|
|
with a hand-written block Cholesky while this uses
|
|
``torch.linalg.solve`` (LU). The two agree to a small tolerance
|
|
(the numerical difference being the whole point of the comparison
|
|
test). This function is never on the production path — the fused
|
|
kernel is the only LP solver at runtime.
|
|
"""
|
|
nc, nv = A.shape
|
|
assert b.shape == (nc,), f"b shape mismatch: {b.shape} vs ({nc},)"
|
|
assert c.shape == (nv,), f"c shape mismatch: {c.shape} vs ({nv},)"
|
|
|
|
x = torch.ones(nv, device=A.device, dtype=torch.float32)
|
|
d_max = torch.tensor(0.0, device=A.device, dtype=torch.float32)
|
|
for _ in range(num_iters):
|
|
ax2 = A * (x * x).unsqueeze(0) # (NC, NV)
|
|
ax2a = ax2 @ A.t() # (NC, NC)
|
|
ax2c = ax2 @ c # (NC,)
|
|
# Match the kernel's 1e-12 pivot clamp via a tiny diagonal jitter so
|
|
# a (near-)singular KKT system stays solvable instead of raising.
|
|
ax2a = ax2a + 1e-12 * torch.eye(nc, device=A.device, dtype=torch.float32)
|
|
delta = torch.linalg.solve(ax2a, ax2c) # (NC,)
|
|
r = A.t() @ delta # (NV,)
|
|
d = x * (c - r) # (NV,)
|
|
d_max = d.max()
|
|
alpha = 0.999 / d_max if d_max > 1e-9 else torch.tensor(1.0, device=A.device)
|
|
x = x * (1.0 - alpha * d)
|
|
|
|
max_residual = (A @ x - b).abs().max()
|
|
converged = (d_max < 0.1) and (0 <= x[-1] < 1e-4) and (max_residual < 0.05)
|
|
if not converged:
|
|
return torch.full((nv,), 0.5, device=A.device, dtype=torch.float32)
|
|
return x
|