NVIDIA / NVIDIA/cuopt

[FEA] PDLP warm start through presolve (today: warm start works only without presolve and loses 3x net) + silent-NaN failure modes

Open
#1,860 1 comment 0 reactions 1 assignee View on GitHub

@tmckayus is already working on this.

Since Sep 6, 2026.

awaiting response
Dominant language
Cuda
Stars
1k
Forks
233
Avg merge
4d 4h
Merged PRs (30d)
95

Description

Is your feature request related to a problem?

We repeatedly solve the same large LP (7.2M rows × 2.27M cols, 14.3M nnz)
where only variable bounds change between solves (a subset of variables is
fixed via lo = hi, and the fixed values move slightly from one solve to the
next). Consecutive optimal solutions are nearly identical, so warm starting
should save most of the work. This is a standard pattern in iterative /
decomposition-style workflows.

With cuOpt 26.04 (nvidia/cuopt:latest-cuda12.9-py3.13, method=1 PDLP,
RTX 3060 12 GB, CUDA 12.9) we measured the whole option space:

  1. DataModel.set_initial_primal_solution / set_initial_dual_solution:
    accepted without error, but the solve returns termination status 0 with
    nan primal/dual objectives after ~4 s — no warning, no diagnostic.
  2. SolverSettings.set_pdlp_warm_start_data from a prior solve of a
    same-shape LP, default solver mode: termination status 0 with nan
    objectives after ~1.3 s — again silent.
  3. Same, but with pdlp_solver_mode = Stable2 (per the docs, warm start is
    supported in Stable2/Fast1) and presolve=0 on both solves so the spaces
    match: warm start works — 1,683 s vs 2,375 s cold in the same
    configuration (~1.4× saving). However:
    • cold with presolve solves the same LP in 547 s: presolve is
      worth ~11× on this instance, and warm-start data is incompatible with it
      (with presolve on: Invalid PDLPWarmStart data ... same amount of variables, since the presolved spaces differ). Net effect:
      warm+no-presolve loses ~3× against cold+presolve, so the feature cannot
      pay for itself today;
    • the warm-started solve reports optimal but its objective lands 0.34 %
      above
      the cold reference for the same LP (759,027 vs 756,484 at
      1e-4 relative tolerance) — far outside tolerance, suggesting the
      warm-started run terminates at a materially different point.
  4. Minor: set_parameter("presolve", False) raises
    ValueError: ... not an integer, and
    set_parameter("pdlp_solver_mode", "Stable2") is accepted at set time but
    fails inside Solve with ValueError: ... not an integer — both want
    ints despite doc examples.

Describe the solution you'd like

In order of usefulness:

  1. Warm start mapped through presolve on the solver side — accept
    warm-start data (or plain primal/dual seed vectors) for a problem with the
    same structure and modified bounds, and translate it into the presolved
    space. That is the version of this feature that can actually win, given
    presolve's ~11× value.
  2. When warm-start input is rejected or unusable (wrong mode, wrong space),
    fail loudly with a clear status/exception instead of returning
    termination status 0 with nan objectives.
  3. Either wire set_initial_primal_solution / set_initial_dual_solution as
    a true initial-iterate seed for LP, or document them as unsupported for LP
    and warn when they are set.

Additional context

All timings are from the same machine and instance and are reproducible; happy
to share exact call sequences and logs. Possibly related: the 0.34 % objective
deviation of the warm-started Stable2 run may deserve its own look.

(Same environment as #1859.)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.