nvidia/cuopt-debugging
Troubleshoot cuOpt LP/MILP problems including errors, wrong results, infeasible solutions, performance issues, and status codes. Use when the user says something isn't working, gets unexpected results, or needs help diagnosing issues.
npx skills add https://github.com/NVIDIA/cuopt-examples --skill cuopt-debugging
Diagnose and fix issues with cuOpt LP/MILP solutions, errors, and performance.
Ask these to understand the problem:
problem.Status.name — what value does it show?Most common cause: Wrong status string case
# ❌ WRONG - "OPTIMAL" never matches, silently fails
if problem.Status.name == "OPTIMAL":
print(problem.ObjValue) # Never runs!
# ✅ CORRECT - use PascalCase
if problem.Status.name in ["Optimal", "FeasibleFound"]:
print(problem.ObjValue)
Diagnostic code:
print(f"Actual status: '{problem.Status.name}'")
print(f"Matches 'Optimal': {problem.Status.name == 'Optimal'}")
print(f"Matches 'OPTIMAL': {problem.Status.name == 'OPTIMAL'}")
Check if variables are actually used:
for var in problem.getVariables():
print(f"{var.VariableName} = {var.Value}")
print(f"Objective: {problem.ObjValue}")
# Or with direct variable references
for var in [x, y, z]:
print(f"{var.VariableName}: {var.getValue()}")
Common causes:
For LP/MILP:
if problem.Status.name in ["PrimalInfeasible", "Infeasible"]:
print("Problem has no feasible solution")
# Review constraints for conflicts
for c in problem.getConstraints():
print(f"{c.ConstraintName}")
Common causes:
# Check how variable was defined
int_var = problem.addVariable(
lb=0, ub=10,
vtype=INTEGER, # Must be INTEGER, not CONTINUOUS
name="count"
)
# Also check if status is actually optimal
if problem.Status.name == "FeasibleFound":
print("Warning: not fully optimal, may have fractional intermediate values")
Problem has no finite optimum:
if problem.Status.name in ["DualInfeasible", "Unbounded"]:
print("Problem is unbounded - objective can improve infinitely")
Common causes:
Building large objectives or constraints with many chained + operations can hit Python recursion limits. Use LinearExpression instead:
from cuopt.linear_programming.problem import LinearExpression
# Instead of: expr = c1*v1 + c2*v2 + ... + cn*vn (many terms)
vars_list = [v1, v2, v3, ...]
coeffs_list = [c1, c2, c3, ...]
expr = LinearExpression(vars_list, coeffs_list, constant=0.0)
problem.setObjective(expr, sense=MINIMIZE)
See the LP/MILP "Building large expressions" section and reference models in the project for examples.
Check problem size:
print(f"Variables: {len(problem.getVariables())}")
print(f"Constraints: {len(problem.getConstraints())}")
Mitigations:
| Status | Meaning |
|--------|---------|
| Optimal | Found optimal solution |
| PrimalFeasible | Found feasible but may not be optimal |
| PrimalInfeasible | No feasible solution exists |
| DualInfeasible | Problem is unbounded |
| TimeLimit | Stopped due to time limit |
| IterationLimit | Stopped due to iteration limit |
| NumericalError | Numerical issues encountered |
| NoTermination | Solver didn't converge |
| Status | Meaning |
|--------|---------|
| Optimal | Found optimal solution |
| FeasibleFound | Found feasible, within gap tolerance |
| Infeasible | No feasible solution exists |
| Unbounded | Problem is unbounded |
| TimeLimit | Stopped due to time limit |
| NoTermination | No solution found yet |
settings = SolverSettings()
settings.set_parameter("log_to_console", 1) # See progress
settings.set_parameter("time_limit", 60) # Don't wait forever
# For MILP, accept good-enough solution
settings.set_parameter("mip_relative_gap", 0.05) # 5% gap
problem.solve(settings)
print(f"Solve time: {problem.SolveTime:.2f} seconds")
□ Status checked with correct case (PascalCase)?
□ All variables have correct vtype (INTEGER vs CONTINUOUS)?
□ Constraint directions correct (<= vs >= vs ==)?
□ Objective sense correct (MINIMIZE vs MAXIMIZE)?
□ Variable bounds specified where needed?
See resources/diagnostic_snippets.md for copy-paste diagnostic code:
When an LP/QP solve returns dual values and you need the *decision* read — which constraint is the binding bottleneck, what relaxing it is worth, and which unused option is the closest near-miss — see resources/interpreting_duals.md. (Integer models / MILP — and quadratic *constraints* — return no usable duals; that reference covers the fallback.)
File a GitHub issue if:
Take nvidia/cuopt-debugging from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.