Skip to content

[BUG] Multi-GPU PDLP sees 0 constraints for problems created with cuOptCreateProblem (row types + RHS) #2042

Description

@0x17

Describe the bug

With multi-GPU PDLP (method=PDLP, num_gpus=-1 or > 1) and presolve off, an LP created with the C API function cuOptCreateProblem (row types + RHS) reaches the solver with 0 constraints. cuOptSolve then fails with CUOPT_VALIDATION_ERROR (4) ("Distributed PDLP from mps requires at least one constraint"). The same LP created with cuOptCreateRangedProblem solves fine, and so does single-GPU PDLP with either form.

CC @mlubin @Bubullzz

Steps/Code to reproduce bug

Reproduces on a single GPU, since num_gpus=-1 means "all visible GPUs" and still selects the multi-GPU PDLP path.

Minimal reproducible example multi-gpu-bug.c:

/* Multi-GPU PDLP (presolve off) sees 0 constraints for problems created with
 * cuOptCreateProblem (row types + RHS).
 * LP: min -x - y  s.t.  x + y <= 1,  0 <= x, y <= 10   (optimum -1) */
#include <cuopt/mathematical_optimization/cuopt_c.h>
#include <stdio.h>

int main(void)
{
    cuopt_int_t offsets[] = {0, 2}, indices[] = {0, 1};
    cuopt_float_t values[] = {1.0, 1.0}, obj[] = {-1.0, -1.0};
    cuopt_float_t lb[] = {0.0, 0.0}, ub[] = {10.0, 10.0};
    char vtypes[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS};
    char sense[] = {CUOPT_LESS_THAN};
    cuopt_float_t rhs[] = {1.0};

    cuOptOptimizationProblem problem;
    cuOptSolverSettings settings;
    cuOptSolution solution;

    /* Works with the ranged form instead:
     *   cuopt_float_t row_lb[] = {-CUOPT_INFINITY}, row_ub[] = {1.0};
     *   cuOptCreateRangedProblem(..., row_lb, row_ub, lb, ub, vtypes, &problem); */
    cuOptCreateProblem(1, 2, CUOPT_MINIMIZE, 0.0, obj, offsets, indices, values,
                       sense, rhs, lb, ub, vtypes, &problem);

    cuopt_int_t num_constraints = -1;
    cuOptGetNumConstraints(problem, &num_constraints);
    printf("Num constraints in problem = %d\n", num_constraints);

    cuOptCreateSolverSettings(&settings);
    cuOptSetIntegerParameter(settings, CUOPT_METHOD, CUOPT_METHOD_PDLP);
    cuOptSetIntegerParameter(settings, CUOPT_PRESOLVE, CUOPT_PRESOLVE_OFF);
    /* all visible GPUs (-1) => multi-GPU PDLP codepath */
    cuOptSetIntegerParameter(settings, CUOPT_NUM_GPUS, -1);

    printf("cuOptSolve status = %d\n", cuOptSolve(problem, settings, &solution));
    return 0;
}

Build and run:

gcc multi-gpu-bug.c -o multi-gpu-bug -I$LIBCUOPT/include -L$LIBCUOPT/lib64 -lcuopt -Wl,-rpath,$LIBCUOPT/lib64
./multi-gpu-bug

Output:

Num constraints in problem = 1
cuOpt version: 26.10.0, git hash: a42e4ca6, host arch: x86_64, device archs: 75-real,80-real,86-real,90a-real,100f-real,120a-real,120
CPU: 13th Gen Intel(R) Core(TM) i7-13700H, threads: 10C/20T, RAM usage: 2.13/23.47GiB
CPU SIMD target: AVX2
CUDA 13.3, device: NVIDIA RTX A1000 6GB Laptop GPU (ID 0), VRAM: 6.00 GiB
CUDA device UUID: 542ae55e-08e0-0257-e3e5-6ac1f557fd25
Solving a problem with 0 constraints, 2 variables (0 integers), and 2 nonzeros
Solving with distributed PDLP on 1 GPUs.
cuOptSolve status = 4

With cuOptCreateRangedProblem, or with num_gpus=1, the solver reports 1 constraint and finds the optimum of -1.

Expected behavior

Multi-GPU PDLP should accept constraints given as row types + RHS, the same way single-GPU PDLP does. For example, op_problem_to_mps_data_model() could build constraint lower/upper bounds from row types + RHS when they are empty, like set_bounds_if_not_set() does.

Separately, the validation message isn't printed: cuOptSolve returns 4, but "Distributed PDLP from mps requires at least one constraint" never appears in the log.

Environment details:

  • System: WSL2, Ubuntu 26.04.1 LTS, x86_64, NVIDIA RTX A1000 6GB Laptop GPU, driver 597.16, CUDA 13.3
  • Method of cuOpt install: pip, nightly wheels cuopt-cu13 / libcuopt-cu13 26.10.0a222.post261002050825 (git hash a42e4ca), used through the C API
  • Beware my machine is only single GPU but looking at solve.cu it appears num_gpus=-1 is enough to make cuOpt exercise the multi GPU codepath.

Additional context

We hit this (see here) in the GAMS/cuOpt link, which uses the C API with multi GPU support added in #1958. Our workaround is to always build the problem with cuOptCreateRangedProblem, as otherwise it would only work fine with num_gpus=1.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions