From 05182ef394aeab39a6a1f54027c1be2ba57d241a Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 27 Feb 2026 12:56:30 -0600 Subject: [PATCH 01/35] update new skill arch --- .github/skills/README.md | 40 -- .github/skills/cuopt-debugging/SKILL.md | 267 ------------- .../resources/diagnostic_snippets.md | 219 ----------- .../skills/cuopt-installation-api-c/SKILL.md | 31 ++ .../resources/verification_examples.md | 0 .../cuopt-installation-api-python/SKILL.md | 66 ++++ .../resources/verification_examples.md | 172 +++++++++ .../skills/cuopt-installation-common/SKILL.md | 28 ++ .../cuopt-installation-developer/SKILL.md | 35 ++ .github/skills/cuopt-installation/SKILL.md | 295 --------------- .github/skills/cuopt-lp-milp-api-c/SKILL.md | 49 +++ .../resources/cli_examples.md | 0 .../resources/examples.md} | 0 .github/skills/cuopt-lp-milp-api-cli/SKILL.md | 57 +++ .../resources/examples.md | 141 +++++++ .../skills/cuopt-lp-milp-api-python/SKILL.md | 116 ++++++ .../resources/examples.md} | 0 .../resources/server_examples.md | 0 .github/skills/cuopt-lp-milp-common/SKILL.md | 29 ++ .github/skills/cuopt-lp-milp/SKILL.md | 240 ------------ .github/skills/cuopt-qp-api-c/SKILL.md | 16 + .github/skills/cuopt-qp-api-cli/SKILL.md | 36 ++ .github/skills/cuopt-qp-api-python/SKILL.md | 58 +++ .../resources/examples.md} | 0 .github/skills/cuopt-qp-common/SKILL.md | 32 ++ .github/skills/cuopt-qp/SKILL.md | 195 ---------- .../skills/cuopt-routing-api-python/SKILL.md | 99 +++++ .../resources/examples.md} | 0 .../resources/server_examples.md | 0 .github/skills/cuopt-routing-common/SKILL.md | 30 ++ .github/skills/cuopt-routing/SKILL.md | 298 --------------- .../skills/cuopt-server-api-python/SKILL.md | 74 ++++ .../resources/lp_milp_examples.md | 0 .../resources/routing_examples.md | 0 .github/skills/cuopt-server-common/SKILL.md | 45 +++ .github/skills/cuopt-server/SKILL.md | 356 ------------------ .github/skills/cuopt-user-rules/SKILL.md | 33 +- 37 files changed, 1130 insertions(+), 1927 deletions(-) delete mode 100644 .github/skills/README.md delete mode 100644 .github/skills/cuopt-debugging/SKILL.md delete mode 100644 .github/skills/cuopt-debugging/resources/diagnostic_snippets.md create mode 100644 .github/skills/cuopt-installation-api-c/SKILL.md rename .github/skills/{cuopt-installation => cuopt-installation-api-c}/resources/verification_examples.md (100%) create mode 100644 .github/skills/cuopt-installation-api-python/SKILL.md create mode 100644 .github/skills/cuopt-installation-api-python/resources/verification_examples.md create mode 100644 .github/skills/cuopt-installation-common/SKILL.md create mode 100644 .github/skills/cuopt-installation-developer/SKILL.md delete mode 100644 .github/skills/cuopt-installation/SKILL.md create mode 100644 .github/skills/cuopt-lp-milp-api-c/SKILL.md rename .github/skills/{cuopt-lp-milp => cuopt-lp-milp-api-c}/resources/cli_examples.md (100%) rename .github/skills/{cuopt-lp-milp/resources/c_api_examples.md => cuopt-lp-milp-api-c/resources/examples.md} (100%) create mode 100644 .github/skills/cuopt-lp-milp-api-cli/SKILL.md create mode 100644 .github/skills/cuopt-lp-milp-api-cli/resources/examples.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/SKILL.md rename .github/skills/{cuopt-lp-milp/resources/python_examples.md => cuopt-lp-milp-api-python/resources/examples.md} (100%) rename .github/skills/{cuopt-lp-milp => cuopt-lp-milp-api-python}/resources/server_examples.md (100%) create mode 100644 .github/skills/cuopt-lp-milp-common/SKILL.md delete mode 100644 .github/skills/cuopt-lp-milp/SKILL.md create mode 100644 .github/skills/cuopt-qp-api-c/SKILL.md create mode 100644 .github/skills/cuopt-qp-api-cli/SKILL.md create mode 100644 .github/skills/cuopt-qp-api-python/SKILL.md rename .github/skills/{cuopt-qp/resources/python_examples.md => cuopt-qp-api-python/resources/examples.md} (100%) create mode 100644 .github/skills/cuopt-qp-common/SKILL.md delete mode 100644 .github/skills/cuopt-qp/SKILL.md create mode 100644 .github/skills/cuopt-routing-api-python/SKILL.md rename .github/skills/{cuopt-routing/resources/python_examples.md => cuopt-routing-api-python/resources/examples.md} (100%) rename .github/skills/{cuopt-routing => cuopt-routing-api-python}/resources/server_examples.md (100%) create mode 100644 .github/skills/cuopt-routing-common/SKILL.md delete mode 100644 .github/skills/cuopt-routing/SKILL.md create mode 100644 .github/skills/cuopt-server-api-python/SKILL.md rename .github/skills/{cuopt-server => cuopt-server-api-python}/resources/lp_milp_examples.md (100%) rename .github/skills/{cuopt-server => cuopt-server-api-python}/resources/routing_examples.md (100%) create mode 100644 .github/skills/cuopt-server-common/SKILL.md delete mode 100644 .github/skills/cuopt-server/SKILL.md diff --git a/.github/skills/README.md b/.github/skills/README.md deleted file mode 100644 index 6ffdb6c5e7..0000000000 --- a/.github/skills/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# cuOpt Skills - -This directory contains AI agent skills for NVIDIA cuOpt. - -## For Agents - -1. **First**: Read `cuopt-user-rules/SKILL.md` for user tasks -2. **Then**: Read the relevant domain skill - -For development tasks, read `cuopt-developer/SKILL.md` (has its own rules). - -## Skills Index - -### Rules -| Skill | Description | -|-------|-------------| -| `cuopt-user-rules/` | Behavior rules for user tasks (read first) | - -### Problem-Solving -| Skill | Description | -|-------|-------------| -| `cuopt-routing/` | VRP, TSP, PDP, fleet optimization | -| `cuopt-lp-milp/` | Linear & mixed-integer programming | -| `cuopt-qp/` | Quadratic programming (beta) | - -### Workflow -| Skill | Description | -|-------|-------------| -| `cuopt-debugging/` | Troubleshooting, errors, diagnostics | -| `cuopt-installation/` | Setup, pip, conda, Docker, GPU | - -### Integration -| Skill | Description | -|-------|-------------| -| `cuopt-server/` | REST API deployment | - -### Development -| Skill | Description | -|-------|-------------| -| `cuopt-developer/` | Contributing to codebase (own rules) | diff --git a/.github/skills/cuopt-debugging/SKILL.md b/.github/skills/cuopt-debugging/SKILL.md deleted file mode 100644 index 649b06387d..0000000000 --- a/.github/skills/cuopt-debugging/SKILL.md +++ /dev/null @@ -1,267 +0,0 @@ ---- -name: cuopt-debugging -description: Troubleshoot cuOpt 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. ---- - -# cuOpt Debugging Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Diagnose and fix issues with cuOpt solutions, errors, and performance. - -## Before You Start: Required Questions - -**Ask these to understand the problem:** - -1. **What's the symptom?** - - Error message? - - Wrong/unexpected results? - - Empty solution? - - Performance too slow? - -2. **What's the status?** - - For LP/MILP: `problem.Status.name` - - For Routing: `solution.get_status()` - - For Server: HTTP response code - -3. **Can you share?** - - The error message (exact text) - - The code that produces it - - Problem size (variables, constraints, locations) - - Share important log messages or status of on-going run. - -## Quick Diagnosis by Symptom - -### "Solution is empty/None but status looks OK" - -**Most common cause: Wrong status string case** - -```python -# ❌ 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:** -```python -print(f"Actual status: '{problem.Status.name}'") -print(f"Matches 'Optimal': {problem.Status.name == 'Optimal'}") -print(f"Matches 'OPTIMAL': {problem.Status.name == 'OPTIMAL'}") -``` - -### "Objective value is wrong/zero" - -**Check if variables are actually used:** -```python -for var in [x, y, z]: - print(f"{var.name}: {var.getValue()}") -print(f"Objective: {problem.ObjValue}") -``` - -**Common causes:** -- Constraints too restrictive (all zeros is feasible) -- Objective coefficients have wrong sign -- Wrong variable in objective - -### "Infeasible" status - -**For LP/MILP:** -```python -if problem.Status.name == "Infeasible": - print("Problem has no feasible solution") - # Check constraints manually - for name in constraint_names: - c = problem.getConstraint(name) - print(f"{name}: {c}") -``` - -**Common causes:** -- Conflicting constraints (x <= 5 AND x >= 10) -- Bounds too tight -- Missing a "slack" variable for soft constraints - -**For Routing:** -```python -if solution.get_status() != 0: - print(f"Error: {solution.get_error_message()}") - infeasible = solution.get_infeasible_orders() - print(f"Infeasible orders: {infeasible.to_list()}") -``` - -**Common routing infeasibility causes:** -- Time windows too tight (earliest > vehicle latest) -- Total demand > total capacity -- Order location unreachable in time - -### "Integer variable has fractional value" - -```python -# 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") -``` - -### Server returns 422 Validation Error - -**Check payload against OpenAPI spec:** - -Common field name mistakes: -``` -❌ transit_time_matrix_data → ✅ travel_time_matrix_data -❌ vehicle_capacities → ✅ capacities -❌ locations → ✅ task_locations -``` - -**Capacity format:** -```json -// ❌ WRONG -"capacities": [[50], [50]] - -// ✅ CORRECT -"capacities": [[50, 50]] -``` - -### OutOfMemoryError - -**Check problem size:** -```python -print(f"Variables: {problem.num_variables}") -print(f"Constraints: {problem.num_constraints}") - -# For routing -print(f"Locations: {n_locations}") -print(f"Orders: {n_orders}") -print(f"Fleet: {n_fleet}") -``` - -**Mitigations:** -- Reduce problem size -- Use sparse constraint matrix -- For routing: reduce time limit, simplify constraints - -### cudf Type Errors - -**Always use explicit dtypes:** -```python -cost_matrix = cost_matrix.astype("float32") -demand = cudf.Series([...], dtype="int32") -order_locations = cudf.Series([...], dtype="int32") -time_windows = cudf.Series([...], dtype="int32") -``` - -### MPS Parsing Fails - -**Check MPS format:** -```bash -head -30 problem.mps -``` - -**Required sections in order:** -1. NAME -2. ROWS -3. COLUMNS -4. RHS -5. (optional) BOUNDS -6. ENDATA - -**Common issues:** -- Missing ENDATA -- Integer markers malformed: `'MARKER'`, `'INTORG'`, `'INTEND'` -- Invalid characters or encoding - -## Status Code Reference - -### LP Status Values -| 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 | - -### MILP Status Values -| 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 | - -### Routing Status Values -| Code | Meaning | -|------|---------| -| 0 | SUCCESS | -| 1 | FAIL | -| 2 | TIMEOUT | -| 3 | EMPTY | - -## Performance Debugging - -### Slow LP/MILP Solve - -```python -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 -``` - -### Slow Routing Solve - -```python -ss = routing.SolverSettings() -ss.set_time_limit(60) # Increase time for better solutions -ss.set_verbose_mode(True) # See progress during solve -``` - -## Diagnostic Checklist - -``` -□ 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)? -□ For QP: using MINIMIZE (not MAXIMIZE)? -□ Data types explicit (float32, int32)? -□ Matrix dimensions match n_locations? -□ Time windows have transit_time_matrix? -``` - -## Diagnostic Code Snippets - -See [resources/diagnostic_snippets.md](resources/diagnostic_snippets.md) for copy-paste diagnostic code: -- Status checking -- Variable inspection -- Constraint analysis -- Routing infeasibility diagnosis -- Server response debugging -- Memory and performance checks - -## When to Escalate - -Switch to **cuopt-developer** if: -- Bug appears to be in cuOpt itself -- Need to examine solver internals - -File a GitHub issue if: -- Reproducible bug with minimal example -- Include: cuOpt version, CUDA version, error message, minimal repro code diff --git a/.github/skills/cuopt-debugging/resources/diagnostic_snippets.md b/.github/skills/cuopt-debugging/resources/diagnostic_snippets.md deleted file mode 100644 index 61f5c19bfd..0000000000 --- a/.github/skills/cuopt-debugging/resources/diagnostic_snippets.md +++ /dev/null @@ -1,219 +0,0 @@ -# Debugging: Diagnostic Snippets - -## LP/MILP Diagnostics - -### Check Status Properly - -```python -# Print actual status value -print(f"Status: '{problem.Status.name}'") -print(f"Status type: {type(problem.Status.name)}") - -# Common mistake: wrong case -print(f"== 'Optimal': {problem.Status.name == 'Optimal'}") # ✅ -print(f"== 'OPTIMAL': {problem.Status.name == 'OPTIMAL'}") # ❌ Always False -``` - -### Inspect Variables - -```python -# Check all variable values -for var in [x, y, z]: - print(f"{var.name}: lb={var.lb}, ub={var.ub}, value={var.getValue()}") - -# Check if integer variables are actually integer -for var in integer_vars: - val = var.getValue() - is_int = abs(val - round(val)) < 1e-6 - print(f"{var.name}: {val} (is_integer: {is_int})") -``` - -### Inspect Constraints - -```python -# Check constraint values -for name in ["constraint1", "constraint2"]: - c = problem.getConstraint(name) - print(f"{name}: dual={c.DualValue}") -``` - -### Check Problem Size - -```python -print(f"Variables: {problem.num_variables}") -print(f"Constraints: {problem.num_constraints}") -``` - -## Routing Diagnostics - -### Check Solution Status - -```python -status = solution.get_status() -print(f"Status code: {status}") -# 0 = SUCCESS -# 1 = FAIL -# 2 = TIMEOUT -# 3 = EMPTY - -if status != 0: - print(f"Message: {solution.get_message()}") - print(f"Error: {solution.get_error_message()}") -``` - -### Find Infeasible Orders - -```python -infeasible = solution.get_infeasible_orders() -if len(infeasible) > 0: - print(f"Infeasible orders: {infeasible.to_list()}") - - # Check why each is infeasible - for order_idx in infeasible.to_list(): - print(f"\nOrder {order_idx}:") - print(f" Location: {order_locations[order_idx]}") - print(f" Time window: [{order_earliest[order_idx]}, {order_latest[order_idx]}]") - print(f" Demand: {demand[order_idx]}") -``` - -### Verify Data Dimensions - -```python -print(f"Cost matrix shape: {cost_matrix.shape}") -print(f"n_locations declared: {dm.n_locations}") -print(f"n_orders: {len(order_locations)}") -print(f"n_fleet: {dm.n_fleet}") - -# Check consistency -assert cost_matrix.shape[0] == cost_matrix.shape[1], "Matrix not square" -assert cost_matrix.shape[0] == dm.n_locations, "Matrix size != n_locations" -``` - -### Check Data Types - -```python -# For numpy arrays, use .dtype directly -# For pandas/cudf DataFrames, use .values.dtype or .to_numpy().dtype -print(f"cost_matrix dtype: {cost_matrix.values.dtype}") # float32 -print(f"order_locations dtype: {order_locations.values.dtype}") # int32 -print(f"demand dtype: {demand.values.dtype}") # int32 -``` - -### Verify Time Windows Feasibility - -```python -# Check for impossible time windows -for i in range(len(order_earliest)): - if order_earliest[i] > order_latest[i]: - print(f"Order {i}: earliest ({order_earliest[i]}) > latest ({order_latest[i]})") - -# Check if orders are reachable from depot in time -depot = 0 -for i in range(len(order_locations)): - loc = order_locations[i] - travel_time = transit_time_matrix.iloc[depot, loc] - if travel_time > order_latest[i]: - print(f"Order {i}: unreachable (travel={travel_time}, latest={order_latest[i]})") -``` - -### Check Capacity Feasibility - -```python -total_demand = demand.sum() -total_capacity = vehicle_capacity.sum() -print(f"Total demand: {total_demand}") -print(f"Total capacity: {total_capacity}") -if total_demand > total_capacity: - print("WARNING: Total demand exceeds total capacity!") -``` - -## Server Diagnostics - -### Check Response Structure - -```python -import json - -response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) -print(f"Status code: {response.status_code}") -print(f"Response: {json.dumps(response.json(), indent=2)}") -``` - -### Validate Payload Against Schema - -```bash -# Get OpenAPI spec -curl -s http://localhost:8000/cuopt.yaml > cuopt_spec.yaml - -# Check payload structure manually -``` - -### Common 422 Error Fixes - -```python -# ❌ Wrong field name -payload = {"transit_time_matrix_data": {...}} - -# ✅ Correct field name -payload = {"travel_time_matrix_data": {...}} - -# ❌ Wrong capacity format -"capacities": [[50], [50]] - -# ✅ Correct capacity format -"capacities": [[50, 50]] -``` - -## Memory Diagnostics - -### Check GPU Memory - -```python -import subprocess -result = subprocess.run(['nvidia-smi'], capture_output=True, text=True) -print(result.stdout) -``` - -### Estimate Problem Memory - -```python -# Rough estimate for routing -n_locations = 1000 -n_fleet = 50 -n_orders = 500 - -# Cost matrix: n_locations² * 4 bytes (float32) -matrix_size = n_locations * n_locations * 4 / 1e9 # GB -print(f"Cost matrix: ~{matrix_size:.2f} GB") -``` - -## Performance Diagnostics - -### Time the Solve - -```python -import time - -start = time.time() -problem.solve(settings) -elapsed = time.time() - start -print(f"Solve time: {elapsed:.2f}s") -``` - -### Enable Solver Logging - -```python -settings = SolverSettings() -settings.set_parameter("log_to_console", 1) -``` - ---- - -## Additional References - -| Topic | Resource | -|-------|----------| -| Python API docstrings | `python/cuopt/cuopt/routing/vehicle_routing.py` | -| LP/MILP Problem class | `python/cuopt/cuopt/linear_programming/problem.py` | -| Server API spec | `docs/cuopt/source/cuopt_spec.yaml` | -| Troubleshooting guide | [NVIDIA cuOpt Docs](https://docs.nvidia.com/cuopt/user-guide/latest/troubleshooting.html) | diff --git a/.github/skills/cuopt-installation-api-c/SKILL.md b/.github/skills/cuopt-installation-api-c/SKILL.md new file mode 100644 index 0000000000..677ffac668 --- /dev/null +++ b/.github/skills/cuopt-installation-api-c/SKILL.md @@ -0,0 +1,31 @@ +--- +name: cuopt-installation-api-c +description: Install cuOpt for C — conda, locate lib/headers, verification. Use when the user is installing or verifying the C API. Standalone; no common skill. +--- + +# cuOpt Installation — C API (user) + +Install cuOpt to *use* it from C. Standalone skill (no separate common). + +## System requirements + +- **GPU**: NVIDIA Compute Capability ≥ 7.0 (Volta+). CUDA 12.x or 13.x. +- **Driver**: Compatible NVIDIA driver. Python and C are separate installables. + +## conda (C / libcuopt) + +```bash +conda install -c rapidsai -c conda-forge -c nvidia cuopt +# libcuopt is provided by the same channel; Python and C are separate packages. +``` + +## Verify C API + +```bash +find $CONDA_PREFIX -name "cuopt_c.h" +find $CONDA_PREFIX -name "libcuopt.so" +``` + +## Examples + +- [verification_examples.md](resources/verification_examples.md) — C API verification diff --git a/.github/skills/cuopt-installation/resources/verification_examples.md b/.github/skills/cuopt-installation-api-c/resources/verification_examples.md similarity index 100% rename from .github/skills/cuopt-installation/resources/verification_examples.md rename to .github/skills/cuopt-installation-api-c/resources/verification_examples.md diff --git a/.github/skills/cuopt-installation-api-python/SKILL.md b/.github/skills/cuopt-installation-api-python/SKILL.md new file mode 100644 index 0000000000..13498a2fef --- /dev/null +++ b/.github/skills/cuopt-installation-api-python/SKILL.md @@ -0,0 +1,66 @@ +--- +name: cuopt-installation-api-python +description: Install cuOpt for Python — pip, conda, Docker, verification. Use when the user is installing or verifying the Python API. Standalone; no common skill. +--- + +# cuOpt Installation — Python (user) + +Install cuOpt to *use* it from Python. Standalone skill (no separate common). + +## System requirements + +- **GPU**: NVIDIA Compute Capability ≥ 7.0 (Volta+). CUDA 12.x or 13.x; match package (cuopt-cu12 / cuopt-cu13). +- **Driver**: Compatible NVIDIA driver. + +## pip (Python) + +```bash +pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13 # or cuopt-cu12 +pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==26.2.*' +``` + +## pip: Server + Client + +```bash +pip install --extra-index-url=https://pypi.nvidia.com cuopt-server-cu12 cuopt-sh-client +``` + +## conda + +```bash +conda install -c rapidsai -c conda-forge -c nvidia cuopt +conda install -c rapidsai -c conda-forge -c nvidia cuopt-server cuopt-sh-client +``` + +## Docker + +```bash +docker pull nvidia/cuopt:latest-cuda12.9-py3.13 +docker run --gpus all -it --rm -p 8000:8000 nvidia/cuopt:latest-cuda12.9-py3.13 +``` + +## Verify Python + +```python +import cuopt +print(cuopt.__version__) +from cuopt import routing +dm = routing.DataModel(n_locations=3, n_fleet=1, n_orders=2) +``` + +## Verify Server + +```bash +python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 & +sleep 5 +curl -s http://localhost:8000/cuopt/health | jq . +``` + +## Common Issues + +- No module 'cuopt' → check `pip list | grep cuopt`, `which python`, reinstall with correct index. +- CUDA not available → `nvidia-smi`, `nvcc --version`, match cuopt-cu12 vs cuopt-cu13 to CUDA. + +## Examples + +- [verification_examples.md](resources/verification_examples.md) — Python and server verification diff --git a/.github/skills/cuopt-installation-api-python/resources/verification_examples.md b/.github/skills/cuopt-installation-api-python/resources/verification_examples.md new file mode 100644 index 0000000000..bd84de80ba --- /dev/null +++ b/.github/skills/cuopt-installation-api-python/resources/verification_examples.md @@ -0,0 +1,172 @@ +# Installation: Verification Examples + +## Verify Python Installation + +```python +# Basic import test +import cuopt +print(f"cuOpt version: {cuopt.__version__}") + +# GPU access test +from cuopt import routing + +dm = routing.DataModel(n_locations=3, n_fleet=1, n_orders=2) +print("DataModel created - GPU access OK") + +# Quick solve test +import cudf +cost_matrix = cudf.DataFrame([[0,1,2],[1,0,1],[2,1,0]], dtype="float32") +dm.add_cost_matrix(cost_matrix) +dm.set_order_locations(cudf.Series([1, 2])) + +solution = routing.Solve(dm, routing.SolverSettings()) +print(f"Solve status: {solution.get_status()}") +print("cuOpt installation verified!") +``` + +## Verify LP/MILP + +```python +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("Test") +x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") +problem.setObjective(x, sense=MAXIMIZE) +problem.addConstraint(x <= 10) + +problem.solve(SolverSettings()) +print(f"Status: {problem.Status.name}") +print(f"x = {x.getValue()}") +print("LP/MILP working!") +``` + +## Verify Server Installation + +```bash +# Start server in background +python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 & +SERVER_PID=$! + +# Wait for startup +sleep 5 + +# Health check +curl -s http://localhost:8000/cuopt/health | jq . + +# Quick routing test +curl -s -X POST "http://localhost:8000/cuopt/request" \ + -H "Content-Type: application/json" \ + -H "CLIENT-VERSION: custom" \ + -d '{ + "cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}}, + "travel_time_matrix_data": {"data": {"0": [[0,1],[1,0]]}}, + "task_data": {"task_locations": [1]}, + "fleet_data": {"vehicle_locations": [[0,0]], "capacities": [[10]]}, + "solver_config": {"time_limit": 1} + }' | jq . + +# Stop server +kill $SERVER_PID +``` + +## Verify C API Installation + +```bash +# Find header +echo "Looking for cuopt_c.h..." +find ${CONDA_PREFIX:-/usr} -name "cuopt_c.h" 2>/dev/null + +# Find library +echo "Looking for libcuopt.so..." +find ${CONDA_PREFIX:-/usr} -name "libcuopt.so" 2>/dev/null + +# Test compile (if gcc available) +cat > /tmp/test_cuopt.c << 'EOF' +#include +#include +int main() { + printf("cuopt_c.h found and compilable\n"); + return 0; +} +EOF + +gcc -I${CONDA_PREFIX}/include -c /tmp/test_cuopt.c -o /tmp/test_cuopt.o && \ + echo "C API headers OK" || echo "C API headers not found" +``` + +## Check System Requirements + +```bash +# GPU check +nvidia-smi + +# CUDA version +nvcc --version + +# Compute capability (need >= 7.0) +nvidia-smi --query-gpu=compute_cap --format=csv,noheader + +# Python version +python --version + +# Available memory +nvidia-smi --query-gpu=memory.total,memory.free --format=csv +``` + +## Check Package Versions + +```python +import importlib.metadata + +packages = ["cuopt-cu12", "cuopt-cu13", "cuopt-server-cu12", "cuopt-server-cu13", "cuopt-sh-client"] +for pkg in packages: + try: + version = importlib.metadata.version(pkg) + print(f"{pkg}: {version}") + except importlib.metadata.PackageNotFoundError: + pass +``` + +## Troubleshooting Commands + +```bash +# Check if cuopt is installed +pip list | grep -i cuopt + +# Check conda packages +conda list | grep -i cuopt + +# Check CUDA runtime +python -c "import torch; print(torch.cuda.is_available())" 2>/dev/null || echo "PyTorch not installed" + +# Check cudf (routing dependency) +python -c "import cudf; print(f'cudf: {cudf.__version__}')" + +# Check rmm (memory manager) +python -c "import rmm; print(f'rmm: {rmm.__version__}')" +``` + +## Docker Verification + +```bash +# Pull and run +docker run --gpus all --rm nvidia/cuopt:latest-cuda12.9-py3.13 python -c " +import cuopt +print(f'cuOpt version: {cuopt.__version__}') +from cuopt import routing +dm = routing.DataModel(n_locations=3, n_fleet=1, n_orders=2) +print('GPU access OK') +" +``` + +--- + +## Additional References + +| Topic | Resource | +|-------|----------| +| Installation Guide | [NVIDIA cuOpt Docs](https://docs.nvidia.com/cuopt/user-guide/latest/installation.html) | +| System Requirements | [cuOpt Requirements](https://docs.nvidia.com/cuopt/user-guide/latest/requirements.html) | +| Docker Images | See `ci/docker/` in this repo | +| Conda Recipes | See `conda/recipes/` in this repo | diff --git a/.github/skills/cuopt-installation-common/SKILL.md b/.github/skills/cuopt-installation-common/SKILL.md new file mode 100644 index 0000000000..4f6bc4e973 --- /dev/null +++ b/.github/skills/cuopt-installation-common/SKILL.md @@ -0,0 +1,28 @@ +--- +name: cuopt-installation-common +description: Install cuOpt — system and environment requirements only. Domain concepts; no install commands or interface guidance. +--- + +# cuOpt Installation (common) + +Domain concepts for installing and running cuOpt. No install commands or interface details here. + +## System requirements + +- **GPU**: NVIDIA with Compute Capability ≥ 7.0 (Volta or newer). Examples: V100, A100, H100, RTX 20xx/30xx/40xx. Not supported: GTX 10xx (Pascal). +- **CUDA**: 12.x or 13.x. Package and runtime must match (e.g. cuopt built for CUDA 12 with a CUDA 12 driver). +- **Driver**: Compatible NVIDIA driver for the CUDA version in use. + +## Required questions (environment) + +Ask these if not already clear: + +1. **Environment** — Local machine with GPU, cloud instance, Docker/Kubernetes, or no GPU (need remote/server)? +2. **CUDA version** — What is installed or planned? (e.g. `nvcc --version`, `nvidia-smi`.) +3. **Usage** — In-process (library/API) vs server (REST)? Which language or runtime (Python, C, server)? +4. **Package manager** — pip, conda, or Docker preferred? + +## Notes + +- Python API and C API are separate installables; having one does not provide the other. +- Server deployment typically uses Docker or a dedicated server package; client can be any language. diff --git a/.github/skills/cuopt-installation-developer/SKILL.md b/.github/skills/cuopt-installation-developer/SKILL.md new file mode 100644 index 0000000000..ddfbe6afaa --- /dev/null +++ b/.github/skills/cuopt-installation-developer/SKILL.md @@ -0,0 +1,35 @@ +--- +name: cuopt-installation-developer +description: Developer installation — build cuOpt from source, run tests. Use when the user wants to set up a dev environment to contribute or modify cuOpt. For contribution rules and PRs, use cuopt-developer after setup. +--- + +# cuOpt Installation — Developer + +Set up an environment to **build cuOpt from source** and run tests. For contribution behavior and PRs, use **cuopt-developer** after the build works. + +## When to use this skill + +- User wants to *build* cuOpt (clone, build deps, build, tests). +- Not for *using* cuOpt (pip/conda) — use `cuopt-installation-api-python` or `cuopt-installation-api-c` instead. + +## Required questions (environment) + +Ask these if not already clear: + +1. **OS and GPU** — Linux? Which CUDA version (e.g. 12.x)? +2. **Goal** — Contributing upstream, or local fork/modification? +3. **Component** — C++/CUDA core, Python bindings, server, docs, or CI? + +## Typical setup (conceptual) + +1. **Clone** the cuOpt repo (and submodules if any). +2. **Build dependencies** — CUDA toolkit, compiler, CMake; see repo docs for the canonical list. +3. **Configure and build** — e.g. top-level `build.sh` or CMake; Debug/Release. +4. **Run tests** — e.g. `pytest` for Python, `ctest` or project test runner for C++. +5. **Optional** — Python env for bindings; pre-commit or style checks. + +Use the repository’s own documentation (README, CONTRIBUTING, or docs/) for exact commands and versions. + +## After setup + +Once the developer can build and run tests, use **cuopt-developer** for behavior rules, code patterns, and contribution workflow (DCO, PRs). diff --git a/.github/skills/cuopt-installation/SKILL.md b/.github/skills/cuopt-installation/SKILL.md deleted file mode 100644 index 981063f477..0000000000 --- a/.github/skills/cuopt-installation/SKILL.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -name: cuopt-installation -description: Install and set up NVIDIA cuOpt including pip, conda, Docker, and GPU requirements. Use when the user asks about installation, setup, environments, CUDA versions, GPU requirements, or getting started. ---- - -# cuOpt Installation Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Set up NVIDIA cuOpt for GPU-accelerated optimization. - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **What's your environment?** - - Local machine with NVIDIA GPU? - - Cloud instance (AWS, GCP, Azure)? - - Docker/Kubernetes? - - No GPU (need cloud solution)? - -2. **What's your CUDA version?** - ```bash - nvcc --version - # or - nvidia-smi - ``` - -3. **What do you need?** - - Python API only? - - REST Server for production? - - C API for embedding? - -4. **Package manager preference?** - - pip - - conda - - Docker - -## System Requirements - -### GPU Requirements -- NVIDIA GPU with Compute Capability >= 7.0 (Volta or newer) -- Supported: V100, A100, H100, RTX 20xx/30xx/40xx, etc. -- NOT supported: GTX 10xx series (Pascal) - -### CUDA Requirements -- CUDA 12.x or CUDA 13.x (match package suffix) -- Compatible NVIDIA driver - -### Check Your System - -```bash -# Check GPU -nvidia-smi - -# Check CUDA version -nvcc --version - -# Check compute capability -nvidia-smi --query-gpu=compute_cap --format=csv -``` - -## Installation Methods - -### pip (Recommended for Python) - -```bash -# For CUDA 13 -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13 - -# For CUDA 12 -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12 - -# With version pinning (recommended for reproducibility) -pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==26.2.*' -``` - -### pip: Server + Client - -```bash -# CUDA 12 example -pip install --extra-index-url=https://pypi.nvidia.com \ - cuopt-server-cu12 cuopt-sh-client - -# With version pinning -pip install --extra-index-url=https://pypi.nvidia.com \ - cuopt-server-cu12==26.02.* cuopt-sh-client==26.02.* -``` - -### conda - -```bash -# Python API -conda install -c rapidsai -c conda-forge -c nvidia cuopt - -# Server + client -conda install -c rapidsai -c conda-forge -c nvidia cuopt-server cuopt-sh-client - -# With version pinning -conda install -c rapidsai -c conda-forge -c nvidia cuopt=26.02.* -``` - -### Docker (Recommended for Server) - -```bash -# Pull image -docker pull nvidia/cuopt:latest-cuda12.9-py3.13 - -# Run server -docker run --gpus all -it --rm \ - -p 8000:8000 \ - -e CUOPT_SERVER_PORT=8000 \ - nvidia/cuopt:latest-cuda12.9-py3.13 - -# Verify -curl http://localhost:8000/cuopt/health -``` - -### Docker: Interactive Python - -```bash -docker run --gpus all -it --rm nvidia/cuopt:latest-cuda12.9-py3.13 python -``` - -## Verification - -### Verify Python Installation - -```python -# Test import -import cuopt -print(f"cuOpt version: {cuopt.__version__}") - -# Test GPU access -from cuopt import routing -dm = routing.DataModel(n_locations=3, n_fleet=1, n_orders=2) -print("GPU access OK") -``` - -### Verify Server Installation - -```bash -# Start server -python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 & - -# Wait and test -sleep 5 -curl -s http://localhost:8000/cuopt/health | jq . -``` - -### Verify C API Installation - -```bash -# Find header -find $CONDA_PREFIX -name "cuopt_c.h" - -# Find library -find $CONDA_PREFIX -name "libcuopt.so" -``` - -## Common Installation Issues - -### "No module named 'cuopt'" - -```bash -# Check if installed -pip list | grep cuopt - -# Check Python environment -which python -echo $CONDA_PREFIX - -# Reinstall -pip uninstall cuopt-cu12 cuopt-cu13 -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12 -``` - -### "CUDA not available" / GPU not detected - -```bash -# Check NVIDIA driver -nvidia-smi - -# Check CUDA toolkit -nvcc --version - -# In Python -import torch # if using PyTorch -print(torch.cuda.is_available()) -``` - -### Version mismatch (CUDA 12 vs 13) - -```bash -# Check installed CUDA -nvcc --version - -# Install matching package -# For CUDA 12.x -pip install cuopt-cu12 - -# For CUDA 13.x -pip install cuopt-cu13 -``` - -### Docker: "could not select device driver" - -```bash -# Install NVIDIA Container Toolkit -# Ubuntu: -distribution=$(. /etc/os-release;echo $ID$VERSION_ID) -curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - -curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \ - sudo tee /etc/apt/sources.list.d/nvidia-docker.list -sudo apt-get update -sudo apt-get install -y nvidia-container-toolkit -sudo systemctl restart docker -``` - -## Environment Setup - -### Create Clean Environment (conda) - -```bash -conda create -n cuopt-env python=3.11 -conda activate cuopt-env -conda install -c rapidsai -c conda-forge -c nvidia cuopt -``` - -### Create Clean Environment (pip/venv) - -```bash -python -m venv cuopt-env -source cuopt-env/bin/activate # Linux/Mac -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12 -``` - -## Cloud Deployment - -### AWS - -- Use p4d.24xlarge (A100) or p3.2xlarge (V100) -- Deep Learning AMI has CUDA pre-installed -- Or use provided Docker image - -### GCP - -- Use a2-highgpu-1g (A100) or n1-standard with T4 -- Deep Learning VM has CUDA pre-installed - -### Azure - -- Use NC-series (T4, A100) -- Data Science VM has CUDA pre-installed - -## Offline Installation - -```bash -# Download wheels on connected machine -pip download --extra-index-url=https://pypi.nvidia.com cuopt-cu12 -d ./wheels - -# Transfer wheels directory to offline machine - -# Install from local wheels -pip install --no-index --find-links=./wheels cuopt-cu12 -``` - -## Upgrade - -```bash -# pip -pip install --upgrade --extra-index-url=https://pypi.nvidia.com cuopt-cu12 - -# conda -conda update -c rapidsai -c conda-forge -c nvidia cuopt - -# Docker -docker pull nvidia/cuopt:latest-cuda12.9-py3.13 -``` - -## Verification Examples - -See [resources/verification_examples.md](resources/verification_examples.md) for: -- Python installation verification -- LP/MILP verification -- Server verification -- C API verification -- System requirements check -- Docker verification - -## Additional Resources - -- Full installation docs: `docs/cuopt/source/cuopt-python/quick-start.rst` -- Server setup: `docs/cuopt/source/cuopt-server/quick-start.rst` -- [NVIDIA cuOpt Documentation](https://docs.nvidia.com/cuopt/user-guide/latest/) diff --git a/.github/skills/cuopt-lp-milp-api-c/SKILL.md b/.github/skills/cuopt-lp-milp-api-c/SKILL.md new file mode 100644 index 0000000000..453c3348f7 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/SKILL.md @@ -0,0 +1,49 @@ +--- +name: cuopt-lp-milp-api-c +description: LP and MILP with cuOpt — C API only. Use with cuopt-lp-milp-common for concepts. Use when the user is embedding LP/MILP in C/C++. +--- + +# cuOpt LP/MILP — C API + +**Concepts:** Read `cuopt-lp-milp-common/SKILL.md` for problem type, when to use, when to escalate. + +This skill is **C only**. For Python, use `cuopt-lp-milp-api-python`. + +## Quick Reference: C API + +```c +#include + +// CSR format for constraints +cuopt_int_t row_offsets[] = {0, 2, 4}; +cuopt_int_t col_indices[] = {0, 1, 0, 1}; +cuopt_float_t values[] = {2.0, 3.0, 4.0, 2.0}; +char var_types[] = {CUOPT_CONTINUOUS, CUOPT_INTEGER}; + +cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, + 0.0, objective_coefficients, + row_offsets, col_indices, values, + constraint_lower, constraint_upper, + var_lower, var_upper, var_types, + &problem +); +cuOptSolve(problem, settings, &solution); +cuOptGetObjectiveValue(solution, &obj_value); +``` + +## Debugging (MPS / C) + +**MPS parsing:** Required sections in order: NAME, ROWS, COLUMNS, RHS, (optional) BOUNDS, ENDATA. Integer markers: `'MARKER'`, `'INTORG'`, `'INTEND'`. + +**OOM or slow:** Check problem size (variables, constraints); use sparse matrix; set time limit and gap tolerance. + +## Examples + +- [examples.md](resources/examples.md) — LP/MILP with build instructions + +For **CLI** (MPS files), use `cuopt-lp-milp-api-cli`. + +## Escalate + +See `cuopt-lp-milp-common` for when to use cuopt-qp-common or cuopt-developer. diff --git a/.github/skills/cuopt-lp-milp/resources/cli_examples.md b/.github/skills/cuopt-lp-milp-api-c/resources/cli_examples.md similarity index 100% rename from .github/skills/cuopt-lp-milp/resources/cli_examples.md rename to .github/skills/cuopt-lp-milp-api-c/resources/cli_examples.md diff --git a/.github/skills/cuopt-lp-milp/resources/c_api_examples.md b/.github/skills/cuopt-lp-milp-api-c/resources/examples.md similarity index 100% rename from .github/skills/cuopt-lp-milp/resources/c_api_examples.md rename to .github/skills/cuopt-lp-milp-api-c/resources/examples.md diff --git a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md new file mode 100644 index 0000000000..0c8d0fb167 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md @@ -0,0 +1,57 @@ +--- +name: cuopt-lp-milp-api-cli +description: LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use with cuopt-lp-milp-common for concepts. Use when the user is solving from MPS via command line. +--- + +# cuOpt LP/MILP — CLI + +**Concepts:** Read `cuopt-lp-milp-common/SKILL.md` for problem type and formulation. + +This skill is **CLI only**. For Python or C, use `cuopt-lp-milp-api-python` or `cuopt-lp-milp-api-c`. + +## Basic usage + +```bash +# Solve LP or MILP from MPS file +cuopt_cli problem.mps + +# With options +cuopt_cli problem.mps --time-limit 120 --mip-relative-tolerance 0.01 +``` + +## Common options + +```bash +cuopt_cli --help + +# Time limit (seconds) +cuopt_cli problem.mps --time-limit 120 + +# MIP gap tolerance (stop when within X% of optimal) +cuopt_cli problem.mps --mip-relative-tolerance 0.001 + +# MIP absolute tolerance +cuopt_cli problem.mps --mip-absolute-tolerance 0.0001 + +# Presolve, iteration limit, method +cuopt_cli problem.mps --presolve --iteration-limit 10000 --method 1 +``` + +## MPS format (required sections, in order) + +1. **NAME** — problem name +2. **ROWS** — N (objective), L/G/E (constraints) +3. **COLUMNS** — variable names, row names, coefficients +4. **RHS** — right-hand side values +5. **BOUNDS** (optional) — LO, UP, FX, BV, LI, UI +6. **ENDATA** + +Integer variables: use `'MARKER' 'INTORG'` before and `'MARKER' 'INTEND'` after the integer columns. + +## Examples + +- [examples.md](resources/examples.md) — LP and MILP MPS examples, format reference, troubleshooting + +## Getting the CLI + +CLI is included with the Python package (`cuopt`). Install via pip or conda (see `cuopt-installation-common` + `cuopt-installation-api-python`); then run `cuopt_cli --help` to verify. diff --git a/.github/skills/cuopt-lp-milp-api-cli/resources/examples.md b/.github/skills/cuopt-lp-milp-api-cli/resources/examples.md new file mode 100644 index 0000000000..b819568af5 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/resources/examples.md @@ -0,0 +1,141 @@ +# LP/MILP: CLI Examples + +## LP from MPS File + +```bash +# Create sample LP in MPS format +cat > production.mps << 'EOF' +* Production Planning: maximize 40*chairs + 30*tables +* s.t. 2*chairs + 3*tables <= 240 (wood) +* 4*chairs + 2*tables <= 200 (labor) +NAME PRODUCTION +ROWS + N PROFIT + L WOOD + L LABOR +COLUMNS + CHAIRS PROFIT -40.0 + CHAIRS WOOD 2.0 + CHAIRS LABOR 4.0 + TABLES PROFIT -30.0 + TABLES WOOD 3.0 + TABLES LABOR 2.0 +RHS + RHS1 WOOD 240.0 + RHS1 LABOR 200.0 +ENDATA +EOF + +# Solve +cuopt_cli production.mps + +# With time limit +cuopt_cli production.mps --time-limit 30 +``` + +## MILP from MPS File + +```bash +# Create MILP with integer variables +cat > facility.mps << 'EOF' +* Facility location with binary variables +NAME FACILITY +ROWS + N COST + G DEMAND1 + L CAP1 + L CAP2 +COLUMNS + MARKER 'MARKER' 'INTORG' + OPEN1 COST 100.0 + OPEN1 CAP1 50.0 + OPEN2 COST 150.0 + OPEN2 CAP2 70.0 + MARKER 'MARKER' 'INTEND' + SHIP11 COST 5.0 + SHIP11 DEMAND1 1.0 + SHIP11 CAP1 -1.0 + SHIP21 COST 7.0 + SHIP21 DEMAND1 1.0 + SHIP21 CAP2 -1.0 +RHS + RHS1 DEMAND1 30.0 +BOUNDS + BV BND1 OPEN1 + BV BND1 OPEN2 + LO BND1 SHIP11 0.0 + LO BND1 SHIP21 0.0 +ENDATA +EOF + +# Solve MILP +cuopt_cli facility.mps --time-limit 60 --mip-relative-tolerance 0.01 +``` + +## Common CLI Options + +```bash +cuopt_cli --help + +# Time limit (seconds) +cuopt_cli problem.mps --time-limit 120 + +# MIP gap tolerance +cuopt_cli problem.mps --mip-relative-tolerance 0.001 + +# MIP absolute tolerance +cuopt_cli problem.mps --mip-absolute-tolerance 0.0001 + +# Presolve, iteration limit, method (0=auto, 1=pdlp, 2=dual_simplex, 3=barrier) +cuopt_cli problem.mps --presolve --iteration-limit 10000 --method 1 +``` + +## MPS Format Reference + +### Required Sections (in order) + +``` +NAME problem_name +ROWS + N objective_row (N = free/objective) + L constraint1 (L = <=) + G constraint2 (G = >=) + E constraint3 (E = ==) +COLUMNS + var1 row1 coefficient +RHS + rhs1 row1 value +ENDATA +``` + +### BOUNDS (optional) + +``` +BOUNDS + LO bnd1 var1 0.0 (lower bound) + UP bnd1 var1 100.0 (upper bound) + FX bnd1 var2 50.0 (fixed) + BV bnd1 var4 (binary 0/1) + LI bnd1 var5 0 (integer lower) + UI bnd1 var5 10 (integer upper) +``` + +### Integer markers + +``` +COLUMNS + MARKER 'MARKER' 'INTORG' + int_var1 OBJ 1.0 + MARKER 'MARKER' 'INTEND' +``` + +## Troubleshooting + +- **Failed to parse MPS** — Check ENDATA, section order (NAME, ROWS, COLUMNS, RHS, [BOUNDS], ENDATA), integer markers. +- **Infeasible** — Check constraint directions (L/G/E) and RHS values. + +## Canonical examples (in repo) + +- `docs/cuopt/source/cuopt-cli/examples/lp/examples/basic_lp_example.sh` +- `docs/cuopt/source/cuopt-cli/examples/milp/examples/basic_milp_example.sh` +- `docs/cuopt/source/cuopt-cli/examples/lp/examples/solver_parameters_example.sh` diff --git a/.github/skills/cuopt-lp-milp-api-python/SKILL.md b/.github/skills/cuopt-lp-milp-api-python/SKILL.md new file mode 100644 index 0000000000..1d836ea84b --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/SKILL.md @@ -0,0 +1,116 @@ +--- +name: cuopt-lp-milp-api-python +description: LP and MILP with cuOpt — Python API only. Use with cuopt-lp-milp-common for concepts. Use when the user is building or solving LP/MILP in Python. +--- + +# cuOpt LP/MILP — Python API + +**Concepts:** Read `cuopt-lp-milp-common/SKILL.md` for problem type, when to use, when to escalate. + +This skill is **Python only**. For C, use `cuopt-lp-milp-api-c`. + +## Quick Reference: Python API + +### LP Example + +```python +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("MyLP") +x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") +y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") +problem.addConstraint(2*x + 3*y <= 120, name="resource_a") +problem.addConstraint(4*x + 2*y <= 100, name="resource_b") +problem.setObjective(40*x + 30*y, sense=MAXIMIZE) +settings = SolverSettings() +settings.set_parameter("time_limit", 60) +problem.solve(settings) + +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Objective: {problem.ObjValue}") + print(f"x = {x.getValue()}, y = {y.getValue()}") +``` + +### MILP Example (integer variables) + +```python +from cuopt.linear_programming.problem import Problem, CONTINUOUS, INTEGER, MINIMIZE + +problem = Problem("FacilityLocation") +open_facility = problem.addVariable(lb=0, ub=1, vtype=INTEGER, name="open") +production = problem.addVariable(lb=0, vtype=CONTINUOUS, name="production") +problem.addConstraint(production <= 1000 * open_facility, name="link") +problem.setObjective(500*open_facility + 2*production, sense=MINIMIZE) +settings = SolverSettings() +settings.set_parameter("time_limit", 120) +settings.set_parameter("mip_relative_gap", 0.01) +problem.solve(settings) + +if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(f"Open: {open_facility.getValue() > 0.5}, Production: {production.getValue()}") +``` + +## CRITICAL: Status Checking (PascalCase) + +```python +# ✅ CORRECT +if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(problem.ObjValue) + +# ❌ WRONG — never matches +if problem.Status.name == "OPTIMAL": + print(problem.ObjValue) +``` + +**LP status:** `Optimal`, `PrimalFeasible`, `PrimalInfeasible`, `TimeLimit`, etc. +**MILP status:** `Optimal`, `FeasibleFound`, `Infeasible`, `TimeLimit`, etc. + +## Solver Settings + +```python +settings = SolverSettings() +settings.set_parameter("time_limit", 60) +settings.set_parameter("mip_relative_gap", 0.01) +settings.set_parameter("log_to_console", 1) +``` + +## Common Issues + +| Problem | Fix | +|---------|-----| +| Status never "Optimal" | Use `"Optimal"` (PascalCase), not `"OPTIMAL"` | +| Integer var fractional | Use `vtype=INTEGER` | +| Infeasible | Check constraint logic and bounds | +| Slow solve | Set time_limit, mip_relative_gap | + +## Debugging + +**Diagnostic — status:** `print(f"Actual status: '{problem.Status.name}'")` + +**Infeasible inspection:** List constraints and check for conflicts: +```python +if problem.Status.name == "Infeasible": + for name in constraint_names: + c = problem.getConstraint(name) + print(f"{name}: {c}") +``` + +**Wrong objective:** Print variable values: `print(f"{var.name}: {var.getValue()}")` and `problem.ObjValue`. + +## Dual Values (LP only) + +```python +if problem.Status.name == "Optimal": + c = problem.getConstraint("resource_a") + print(f"Shadow price: {c.DualValue}") +``` + +## Examples + +- [examples.md](resources/examples.md) — LP, MILP, knapsack, transportation +- [server_examples.md](resources/server_examples.md) — REST from Python + +## Escalate + +See `cuopt-lp-milp-common` for when to use cuopt-qp-common or cuopt-developer. diff --git a/.github/skills/cuopt-lp-milp/resources/python_examples.md b/.github/skills/cuopt-lp-milp-api-python/resources/examples.md similarity index 100% rename from .github/skills/cuopt-lp-milp/resources/python_examples.md rename to .github/skills/cuopt-lp-milp-api-python/resources/examples.md diff --git a/.github/skills/cuopt-lp-milp/resources/server_examples.md b/.github/skills/cuopt-lp-milp-api-python/resources/server_examples.md similarity index 100% rename from .github/skills/cuopt-lp-milp/resources/server_examples.md rename to .github/skills/cuopt-lp-milp-api-python/resources/server_examples.md diff --git a/.github/skills/cuopt-lp-milp-common/SKILL.md b/.github/skills/cuopt-lp-milp-common/SKILL.md new file mode 100644 index 0000000000..45acfef614 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-common/SKILL.md @@ -0,0 +1,29 @@ +--- +name: cuopt-lp-milp-common +description: LP and MILP with cuOpt — problem type and formulation only. Domain concepts; no API code and no interface or escalation guidance. +--- + +# cuOpt LP/MILP (common) + +Domain concepts for linear and mixed-integer linear programming. No API or interface details here. + +## What is LP / MILP + +- **LP**: Linear objective, linear constraints, continuous variables. +- **MILP**: Same plus some integer or binary variables (e.g. scheduling, facility location, selection). + +## Required questions (problem formulation) + +Ask these if not already clear: + +1. **Decision variables** — What are they? Bounds? +2. **Objective** — Minimize or maximize? Linear expression in the variables? +3. **Constraints** — Linear inequalities/equalities? Names and meaning? +4. **Variable types** — All continuous (LP) or some integer/binary (MILP)? + +## Typical modeling elements + +- **Continuous variables** — production amounts, flow, etc. +- **Binary variables** — open/close, yes/no (e.g. facility open, item selected). +- **Linking constraints** — e.g. production only if facility open (Big-M or indicator). +- **Resource constraints** — linear cap on usage (materials, time, capacity). diff --git a/.github/skills/cuopt-lp-milp/SKILL.md b/.github/skills/cuopt-lp-milp/SKILL.md deleted file mode 100644 index 58dfb4f09a..0000000000 --- a/.github/skills/cuopt-lp-milp/SKILL.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -name: cuopt-lp-milp -description: Solve Linear Programming (LP) and Mixed-Integer Linear Programming (MILP) with NVIDIA cuOpt. Use when the user asks about optimization with linear constraints, integer variables, scheduling, resource allocation, facility location, or production planning. ---- - -# cuOpt LP/MILP Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Model and solve linear and mixed-integer linear programs using NVIDIA cuOpt's GPU-accelerated solver. - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **Problem formulation?** - - What are the decision variables? - - What is the objective (minimize/maximize what)? - - What are the constraints? - -2. **Variable types?** - - All continuous (LP)? - - Some integer/binary (MILP)? - -3. **Interface preference?** - - Python API (recommended for modeling) - - C API (native embedding) - - CLI (quick solve from MPS file) - - REST Server (production deployment) - -4. **Do you have an MPS file?** - - If yes, CLI or C API may be simpler - - If no, Python API is best for building the model - -## Interface Support - -| Interface | LP | MILP | -|-----------|:--:|:----:| -| Python | ✓ | ✓ | -| C API | ✓ | ✓ | -| CLI | ✓ | ✓ | -| REST | ✓ | ✓ | - -## Quick Reference: Python API - -### LP Example - -```python -from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE -from cuopt.linear_programming.solver_settings import SolverSettings - -# Create problem -problem = Problem("MyLP") - -# Decision variables -x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") -y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") - -# Constraints -problem.addConstraint(2*x + 3*y <= 120, name="resource_a") -problem.addConstraint(4*x + 2*y <= 100, name="resource_b") - -# Objective -problem.setObjective(40*x + 30*y, sense=MAXIMIZE) - -# Solve -settings = SolverSettings() -settings.set_parameter("time_limit", 60) -problem.solve(settings) - -# Check status (CRITICAL: use PascalCase!) -if problem.Status.name in ["Optimal", "PrimalFeasible"]: - print(f"Objective: {problem.ObjValue}") - print(f"x = {x.getValue()}") - print(f"y = {y.getValue()}") -``` - -### MILP Example (with integer variables) - -```python -from cuopt.linear_programming.problem import Problem, CONTINUOUS, INTEGER, MINIMIZE - -problem = Problem("FacilityLocation") - -# Binary variable (integer with bounds 0-1) -open_facility = problem.addVariable(lb=0, ub=1, vtype=INTEGER, name="open") - -# Continuous variable -production = problem.addVariable(lb=0, vtype=CONTINUOUS, name="production") - -# Linking constraint: can only produce if facility is open -problem.addConstraint(production <= 1000 * open_facility, name="link") - -# Objective: fixed cost + variable cost -problem.setObjective(500*open_facility + 2*production, sense=MINIMIZE) - -# MILP-specific settings -settings = SolverSettings() -settings.set_parameter("time_limit", 120) -settings.set_parameter("mip_relative_gap", 0.01) # 1% optimality gap - -problem.solve(settings) - -# Check status -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(f"Open facility: {open_facility.getValue() > 0.5}") - print(f"Production: {production.getValue()}") -``` - -## CRITICAL: Status Checking - -**Status values use PascalCase, NOT ALL_CAPS:** - -```python -# ✅ CORRECT -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(problem.ObjValue) - -# ❌ WRONG - will silently fail! -if problem.Status.name == "OPTIMAL": # Never matches! - print(problem.ObjValue) -``` - -**LP Status Values:** `Optimal`, `NoTermination`, `NumericalError`, `PrimalInfeasible`, `DualInfeasible`, `IterationLimit`, `TimeLimit`, `PrimalFeasible` - -**MILP Status Values:** `Optimal`, `FeasibleFound`, `Infeasible`, `Unbounded`, `TimeLimit`, `NoTermination` - -## Quick Reference: C API - -```c -#include - -// CSR format for constraints -cuopt_int_t row_offsets[] = {0, 2, 4}; -cuopt_int_t col_indices[] = {0, 1, 0, 1}; -cuopt_float_t values[] = {2.0, 3.0, 4.0, 2.0}; - -// Variable types -char var_types[] = {CUOPT_CONTINUOUS, CUOPT_INTEGER}; - -cuOptCreateRangedProblem( - num_constraints, num_variables, CUOPT_MINIMIZE, - 0.0, // objective offset - objective_coefficients, - row_offsets, col_indices, values, - constraint_lower, constraint_upper, - var_lower, var_upper, - var_types, - &problem -); - -cuOptSolve(problem, settings, &solution); -cuOptGetObjectiveValue(solution, &obj_value); -``` - -## Quick Reference: CLI - -```bash -# Solve LP from MPS file -cuopt_cli problem.mps - -# With options -cuopt_cli problem.mps --time-limit 120 --mip-relative-tolerance 0.01 -``` - -## Common Modeling Patterns - -### Binary Selection -```python -# Select exactly k items from n -items = [problem.addVariable(lb=0, ub=1, vtype=INTEGER) for _ in range(n)] -problem.addConstraint(sum(items) == k) -``` - -### Big-M Linking -```python -# If y=1, then x <= 100; if y=0, x can be anything up to M -M = 10000 -problem.addConstraint(x <= 100 + M*(1 - y)) -``` - -### Piecewise Linear (SOS2) -```python -# Approximate nonlinear function with breakpoints -# Use lambda variables that sum to 1, at most 2 adjacent non-zero -``` - -## Solver Settings - -```python -settings = SolverSettings() - -# Time limit -settings.set_parameter("time_limit", 60) - -# MILP gap tolerance (stop when within X% of optimal) -settings.set_parameter("mip_relative_gap", 0.01) - -# Logging -settings.set_parameter("log_to_console", 1) -``` - -## Common Issues - -| Problem | Likely Cause | Fix | -|---------|--------------|-----| -| Status never "OPTIMAL" | Using wrong case | Use `"Optimal"` not `"OPTIMAL"` | -| Integer var has fractional value | Defined as CONTINUOUS | Use `vtype=INTEGER` | -| Infeasible | Conflicting constraints | Check constraint logic | -| Unbounded | Missing bounds | Add variable bounds | -| Slow solve | Large problem | Set time limit, increase gap tolerance | - -## Getting Dual Values (LP only) - -```python -if problem.Status.name == "Optimal": - constraint = problem.getConstraint("resource_a") - shadow_price = constraint.DualValue - print(f"Shadow price: {shadow_price}") -``` - -## Examples - -See `resources/` for complete examples: -- [Python API](resources/python_examples.md) — LP, MILP, knapsack, transportation -- [C API](resources/c_api_examples.md) — LP/MILP with build instructions -- [CLI](resources/cli_examples.md) — MPS file format and commands -- [REST Server](resources/server_examples.md) — curl and Python requests - -## When to Escalate - -Switch to **cuopt-qp** if: -- Objective has quadratic terms (x², x*y) - -Switch to **cuopt-debugging** if: -- Infeasible and can't determine why -- Numerical issues - -Switch to **cuopt-developer** if: -- User wants to modify solver internals diff --git a/.github/skills/cuopt-qp-api-c/SKILL.md b/.github/skills/cuopt-qp-api-c/SKILL.md new file mode 100644 index 0000000000..be2ba8271d --- /dev/null +++ b/.github/skills/cuopt-qp-api-c/SKILL.md @@ -0,0 +1,16 @@ +--- +name: cuopt-qp-api-c +description: Quadratic Programming (QP) with cuOpt — C API. Use with cuopt-qp-common for concepts. Use when the user is embedding QP in C/C++. +--- + +# cuOpt QP — C API + +**Concepts:** Read `cuopt-qp-common/SKILL.md` for when QP applies, minimize-only, when to escalate. + +This skill is **C only**. For Python (beta), use `cuopt-qp-api-python`. + +QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic objectives. See LP/MILP C API docs and build instructions in `cuopt-lp-milp-api-c` for the base setup; then use the QP-specific creation/solve calls from the cuOpt C headers. + +## Escalate + +See `cuopt-qp-common` for when to use cuopt-lp-milp-common or cuopt-developer. diff --git a/.github/skills/cuopt-qp-api-cli/SKILL.md b/.github/skills/cuopt-qp-api-cli/SKILL.md new file mode 100644 index 0000000000..fdb1257515 --- /dev/null +++ b/.github/skills/cuopt-qp-api-cli/SKILL.md @@ -0,0 +1,36 @@ +--- +name: cuopt-qp-api-cli +description: QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use with cuopt-qp-common for concepts. Use when the user is solving QP from the command line. +--- + +# cuOpt QP — CLI + +**Concepts:** Read `cuopt-qp-common/SKILL.md` for when QP applies and the **minimize-only** rule. + +This skill is **CLI only** for QP. For Python or C, use `cuopt-qp-api-python` or `cuopt-qp-api-c`. + +## QP via CLI + +cuOpt CLI supports QP (quadratic objectives). Use the same `cuopt_cli` tool; input format and options may extend the LP/MILP MPS workflow to allow quadratic terms (see repo docs or `cuopt_cli --help` for QP-specific options). + +**Important:** QP objectives must be **minimization**. For maximization, negate the objective. + +## Basic usage + +```bash +# Solve QP (syntax may match or extend LP/MILP CLI; check --help) +cuopt_cli problem.mps + +# With time limit +cuopt_cli problem.mps --time-limit 60 +``` + +Check `cuopt_cli --help` and the repository documentation (e.g. `docs/cuopt/source/cuopt-cli/`) for QP file format and any QP-specific flags. + +## Getting the CLI + +CLI is included with the Python package (`cuopt`). Install via pip or conda (see `cuopt-installation-api-python`); then run `cuopt_cli --help` to verify. + +## Escalate + +See `cuopt-qp-common` for when to use cuopt-lp-milp-common or cuopt-developer. diff --git a/.github/skills/cuopt-qp-api-python/SKILL.md b/.github/skills/cuopt-qp-api-python/SKILL.md new file mode 100644 index 0000000000..1b69229a9e --- /dev/null +++ b/.github/skills/cuopt-qp-api-python/SKILL.md @@ -0,0 +1,58 @@ +--- +name: cuopt-qp-api-python +description: Quadratic Programming (QP) with cuOpt — Python API only (beta). Use with cuopt-qp-common for concepts. Use when the user is building or solving QP in Python. +--- + +# cuOpt QP — Python API (beta) + +**Concepts:** Read `cuopt-qp-common/SKILL.md` for when QP applies, minimize-only, when to escalate. + +This skill is **Python only**. For C, use `cuopt-qp-api-c`. **QP is beta.** + +## CRITICAL: MINIMIZE only + +```python +# ❌ WRONG +problem.setObjective(x*x + y*y, sense=MAXIMIZE) + +# ✅ CORRECT — negate for maximization +problem.setObjective(-(x*x + y*y), sense=MINIMIZE) +``` + +## Portfolio Example + +```python +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("Portfolio") +x1 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_a") +x2 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_b") +x3 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_c") +problem.setObjective( + 0.04*x1*x1 + 0.02*x2*x2 + 0.01*x3*x3 + 0.02*x1*x2 + 0.01*x1*x3 + 0.016*x2*x3, + sense=MINIMIZE +) +problem.addConstraint(x1 + x2 + x3 == 1, name="budget") +problem.addConstraint(r1*x1 + r2*x2 + r3*x3 >= 0.08, name="min_return") +problem.solve(SolverSettings()) +``` + +## Status (PascalCase) + +```python +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(problem.ObjValue) +``` + +## Debugging + +**Diagnostic:** `print(f"Actual status: '{problem.Status.name}'")`. For numerical issues, check Q is PSD and variables are scaled. + +## Examples + +- [examples.md](resources/examples.md) — portfolio, least squares, maximization workaround + +## Escalate + +See `cuopt-qp-common` for when to use cuopt-lp-milp-common or cuopt-developer. diff --git a/.github/skills/cuopt-qp/resources/python_examples.md b/.github/skills/cuopt-qp-api-python/resources/examples.md similarity index 100% rename from .github/skills/cuopt-qp/resources/python_examples.md rename to .github/skills/cuopt-qp-api-python/resources/examples.md diff --git a/.github/skills/cuopt-qp-common/SKILL.md b/.github/skills/cuopt-qp-common/SKILL.md new file mode 100644 index 0000000000..6c3b2018d5 --- /dev/null +++ b/.github/skills/cuopt-qp-common/SKILL.md @@ -0,0 +1,32 @@ +--- +name: cuopt-qp-common +description: Quadratic Programming (QP) with cuOpt — problem form and constraints only. Domain concepts; no API or interface guidance. QP is beta. +--- + +# cuOpt QP (common) + +Domain concepts for quadratic programming. No API or interface details here. **QP support in cuOpt is currently in beta.** + +## What is QP + +- **Objective**: Quadratic in the variables (e.g. x², x·y terms). Example: portfolio variance xᵀQx. +- **Constraints**: Linear only. cuOpt does not support quadratic constraints. + +## Important domain rule: minimize only + +QP objectives must be **minimization**. To maximize a quadratic expression, negate it and minimize; then negate the optimal value. + +## Required questions (problem formulation) + +Ask these if not already clear: + +1. **Objective** — Does it have squared or cross terms (x², x·y)? If purely linear, use LP/MILP instead. +2. **Minimize or maximize?** — If maximize, user must negate objective and minimize. +3. **Convexity** — For minimization, the quadratic form (matrix Q) should be positive semi-definite for well-posed problems. +4. **Constraints** — All linear (no quadratic constraints)? + +## Typical use cases + +- Portfolio optimization (minimize variance subject to return and budget). +- Least squares (minimize ‖Ax − b‖²). +- Other quadratic objectives with linear constraints. diff --git a/.github/skills/cuopt-qp/SKILL.md b/.github/skills/cuopt-qp/SKILL.md deleted file mode 100644 index 5fd7d83af6..0000000000 --- a/.github/skills/cuopt-qp/SKILL.md +++ /dev/null @@ -1,195 +0,0 @@ ---- -name: cuopt-qp -description: Solve Quadratic Programming (QP) with NVIDIA cuOpt. Use when the user asks about quadratic objectives, portfolio optimization, variance minimization, or least squares problems. Note that QP support is currently in beta. ---- - -# cuOpt QP Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Model and solve quadratic programs using NVIDIA cuOpt. **QP support is currently in beta.** - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **Is this actually QP?** - - Does the objective have x² or x*y terms? - - Are constraints still linear? - - (If constraints are also quadratic, cuOpt doesn't support that) - -2. **Minimize or maximize?** - - ⚠️ **QP only supports MINIMIZE** - - For maximization, negate the objective - -3. **Is the quadratic form convex?** - - Q matrix should be positive semi-definite for minimization - - Non-convex QP may not solve correctly - -## Critical Constraints - -### ⚠️ MINIMIZE ONLY - -**QP objectives MUST be minimization.** The solver rejects maximize for QP. - -```python -# ❌ WRONG - will fail -problem.setObjective(x*x + y*y, sense=MAXIMIZE) - -# ✅ CORRECT - minimize instead -# To maximize f(x), minimize -f(x) -problem.setObjective(-(x*x + y*y), sense=MINIMIZE) -``` - -### Interface Support - -| Interface | QP Support | -|-----------|:----------:| -| Python | ✓ (beta) | -| C API | ✓ | -| REST | ✗ | -| CLI | ✗ | - -## Quick Reference: Python API - -### Portfolio Optimization Example - -```python -""" -Minimize portfolio variance (risk): - minimize x^T * Q * x - subject to sum(x) = 1 (fully invested) - r^T * x >= target (minimum return) - x >= 0 (no short selling) -""" -from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE -from cuopt.linear_programming.solver_settings import SolverSettings - -problem = Problem("Portfolio") - -# Portfolio weights (decision variables) -x1 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_a") -x2 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_b") -x3 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_c") - -# Expected returns -r1, r2, r3 = 0.12, 0.08, 0.05 - -# Quadratic objective: variance = x^T * Q * x -# Q = [[0.04, 0.01, 0.005], -# [0.01, 0.02, 0.008], -# [0.005, 0.008, 0.01]] -# Expanded: 0.04*x1² + 0.02*x2² + 0.01*x3² + 2*0.01*x1*x2 + ... -problem.setObjective( - 0.04*x1*x1 + 0.02*x2*x2 + 0.01*x3*x3 + - 0.02*x1*x2 + 0.01*x1*x3 + 0.016*x2*x3, - sense=MINIMIZE # MUST be minimize for QP! -) - -# Linear constraints -problem.addConstraint(x1 + x2 + x3 == 1, name="budget") -problem.addConstraint(r1*x1 + r2*x2 + r3*x3 >= 0.08, name="min_return") - -# Solve -settings = SolverSettings() -settings.set_parameter("time_limit", 60) -problem.solve(settings) - -# Check results -if problem.Status.name in ["Optimal", "PrimalFeasible"]: - print(f"Variance: {problem.ObjValue:.6f}") - print(f"Std Dev: {problem.ObjValue**0.5:.4f}") - print(f"Allocation: A={x1.getValue():.2%}, B={x2.getValue():.2%}, C={x3.getValue():.2%}") -``` - -### Least Squares Example - -```python -""" -Minimize ||Ax - b||² = x^T*A^T*A*x - 2*b^T*A*x + b^T*b -""" -problem = Problem("LeastSquares") - -x = problem.addVariable(lb=-100, ub=100, vtype=CONTINUOUS, name="x") -y = problem.addVariable(lb=-100, ub=100, vtype=CONTINUOUS, name="y") - -# Quadratic objective: (x-3)² + (y-4)² = x² + y² - 6x - 8y + 25 -problem.setObjective( - x*x + y*y - 6*x - 8*y + 25, - sense=MINIMIZE -) - -problem.solve(SolverSettings()) - -print(f"x = {x.getValue()}") # Should be ~3 -print(f"y = {y.getValue()}") # Should be ~4 -``` - -## Formulating Quadratic Objectives - -### From Covariance Matrix - -Given covariance matrix Q and weights x: -``` -variance = x^T * Q * x = Σᵢ Σⱼ Qᵢⱼ * xᵢ * xⱼ -``` - -Expand manually: -```python -# Q = [[a, b], [b, c]] -# x^T * Q * x = a*x1² + 2b*x1*x2 + c*x2² -objective = a*x1*x1 + 2*b*x1*x2 + c*x2*x2 -``` - -### Maximization Workaround - -To maximize `f(x) = -x² + 4x`: -```python -# maximize -x² + 4x -# = minimize -(-x² + 4x) -# = minimize x² - 4x -problem.setObjective(x*x - 4*x, sense=MINIMIZE) -# Then negate the objective value for the true maximum -true_max = -problem.ObjValue -``` - -## Status Checking - -Same as LP/MILP - use PascalCase: - -```python -if problem.Status.name in ["Optimal", "PrimalFeasible"]: - print(f"Optimal variance: {problem.ObjValue}") -``` - -## Common Issues - -| Problem | Likely Cause | Fix | -|---------|--------------|-----| -| "Quadratic problems must be minimized" | Using MAXIMIZE | Use MINIMIZE, negate objective | -| Poor convergence | Non-convex Q | Ensure Q is positive semi-definite | -| NumericalError | Ill-conditioned Q | Scale variables, regularize | -| Slow solve | Large dense Q | Check if problem can be simplified | - -## Solver Notes - -- QP uses **Barrier method** internally (different from LP/MILP defaults) -- May be more sensitive to numerical issues than LP -- Beta status means API may change in future versions - -## Examples - -See `resources/` for complete examples: -- [Python API](resources/python_examples.md) — portfolio, least squares, maximization workaround - -## When to Escalate - -Switch to **cuopt-lp-milp** if: -- Objective is actually linear (no x² or x*y terms) - -Switch to **cuopt-debugging** if: -- Numerical errors -- Unexpected results - -Switch to **cuopt-developer** if: -- Need features not in beta QP diff --git a/.github/skills/cuopt-routing-api-python/SKILL.md b/.github/skills/cuopt-routing-api-python/SKILL.md new file mode 100644 index 0000000000..b6793cf80f --- /dev/null +++ b/.github/skills/cuopt-routing-api-python/SKILL.md @@ -0,0 +1,99 @@ +--- +name: cuopt-routing-api-python +description: Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use with cuopt-routing-common for concepts. Use when the user is building or solving routing in Python. +--- + +# cuOpt Routing — Python API + +**Concepts:** Read `cuopt-routing-common/SKILL.md` for problem types, when to use, when to escalate. + +This skill is **Python only**. Routing has no C API in cuOpt. + +## Minimal VRP Example + +```python +import cudf +from cuopt import routing + +cost_matrix = cudf.DataFrame([...], dtype="float32") +dm = routing.DataModel(n_locations=4, n_fleet=2, n_orders=3) +dm.add_cost_matrix(cost_matrix) +dm.set_order_locations(cudf.Series([1, 2, 3])) +solution = routing.Solve(dm, routing.SolverSettings()) + +if solution.get_status() == 0: + solution.display_routes() +``` + +## Adding Constraints + +```python +# Time windows +dm.add_transit_time_matrix(transit_time_matrix) +dm.set_order_time_windows(earliest_series, latest_series) + +# Capacities +dm.add_capacity_dimension("weight", demand_series, capacity_series) +dm.set_order_service_times(service_times) +dm.set_vehicle_locations(start_locations, end_locations) +dm.set_vehicle_time_windows(earliest_start, latest_return) + +# Pickup-delivery pairs +dm.set_pickup_delivery_pairs(pickup_indices, delivery_indices) + +# Precedence +dm.add_order_precedence(node_id=2, preceding_nodes=np.array([0, 1])) +``` + +## Solution Checking + +```python +status = solution.get_status() # 0=SUCCESS, 1=FAIL, 2=TIMEOUT, 3=EMPTY +if status == 0: + route_df = solution.get_route() + total_cost = solution.get_total_objective() +else: + print(solution.get_error_message()) + print(solution.get_infeasible_orders().to_list()) +``` + +## Data Types (use explicit dtypes) + +```python +cost_matrix = cost_matrix.astype("float32") +order_locations = cudf.Series([...], dtype="int32") +demand = cudf.Series([...], dtype="int32") +``` + +## Solver Settings + +```python +ss = routing.SolverSettings() +ss.set_time_limit(30) +ss.set_verbose_mode(True) +ss.set_error_logging_mode(True) +``` + +## Common Issues + +| Problem | Fix | +|---------|-----| +| Empty solution | Widen time windows or check travel times | +| Infeasible orders | Increase fleet or capacity | +| Status != 0 with time windows | Add `add_transit_time_matrix()` | +| Wrong cost | Check cost_matrix is symmetric | + +## Debugging + +**When status != 0:** `print(solution.get_error_message())` and `print(solution.get_infeasible_orders().to_list())` to see which orders are infeasible. + +**Data types:** Use explicit dtypes (float32, int32) for matrices and series to avoid silent errors. + +## Examples + +- [examples.md](resources/examples.md) — VRP, PDP, multi-depot +- [server_examples.md](resources/server_examples.md) — REST client (curl, Python) + +## Escalate + +See `cuopt-routing-common` for when to use cuopt-developer. diff --git a/.github/skills/cuopt-routing/resources/python_examples.md b/.github/skills/cuopt-routing-api-python/resources/examples.md similarity index 100% rename from .github/skills/cuopt-routing/resources/python_examples.md rename to .github/skills/cuopt-routing-api-python/resources/examples.md diff --git a/.github/skills/cuopt-routing/resources/server_examples.md b/.github/skills/cuopt-routing-api-python/resources/server_examples.md similarity index 100% rename from .github/skills/cuopt-routing/resources/server_examples.md rename to .github/skills/cuopt-routing-api-python/resources/server_examples.md diff --git a/.github/skills/cuopt-routing-common/SKILL.md b/.github/skills/cuopt-routing-common/SKILL.md new file mode 100644 index 0000000000..b906e91618 --- /dev/null +++ b/.github/skills/cuopt-routing-common/SKILL.md @@ -0,0 +1,30 @@ +--- +name: cuopt-routing-common +description: Vehicle routing (VRP, TSP, PDP) with cuOpt — problem types and data requirements only. Domain concepts; no API or interface guidance. +--- + +# cuOpt Routing (common) + +Domain concepts for vehicle routing. No API or interface details here. + +## What is routing + +- **TSP**: Single vehicle, visit all locations once (e.g. shortest tour). +- **VRP**: Multiple vehicles, capacity and/or time limits; assign orders to vehicles and sequence stops. +- **PDP**: Pickup and delivery pairs; pickup must be visited before the corresponding delivery. + +## Required questions (problem and data) + +Ask these if not already clear: + +1. **Problem type** — TSP, VRP, or PDP? +2. **Locations** — How many? Depot(s)? Cost or distance between pairs (matrix or derived)? +3. **Orders / tasks** — Which locations must be visited? Demand or service per stop? +4. **Fleet** — Number of vehicles, capacity per vehicle (and per dimension if multiple), start/end locations? +5. **Constraints** — Time windows (earliest/latest arrival), service times, precedence (order A before B)? + +## Typical data + +- Cost or distance matrix (or travel-time matrix). +- Order locations and, for VRP, demand per order. +- Vehicle capacities and optional time windows for vehicles and orders. diff --git a/.github/skills/cuopt-routing/SKILL.md b/.github/skills/cuopt-routing/SKILL.md deleted file mode 100644 index e634da7dba..0000000000 --- a/.github/skills/cuopt-routing/SKILL.md +++ /dev/null @@ -1,298 +0,0 @@ ---- -name: cuopt-routing -description: Solve vehicle routing problems (VRP, TSP, PDP) with NVIDIA cuOpt. Use when the user asks about delivery optimization, fleet routing, time windows, capacities, pickup-delivery pairs, or traveling salesman problems. ---- - -# cuOpt Routing Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Model and solve vehicle routing problems using NVIDIA cuOpt's GPU-accelerated solver. - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **Problem type?** - - TSP (single vehicle, visit all locations) - - VRP (multiple vehicles, capacity constraints) - - PDP (pickup and delivery pairs) - -2. **What constraints?** - - Time windows (earliest/latest arrival)? - - Vehicle capacities? - - Service times at locations? - - Multiple depots? - - Vehicle-specific start/end locations? - -3. **What data do you have?** - - Cost/distance matrix or coordinates? - - Demand per location? - - Fleet size fixed or to optimize? - -4. **Interface preference?** - - Python API (in-process) - - REST Server (production/async) - -## Interface Support - -| Interface | Routing Support | -|-----------|:---------------:| -| Python | ✓ | -| REST | ✓ | -| C API | ✗ | -| CLI | ✗ | - -## Quick Reference: Python API - -### Minimal VRP Example - -```python -import cudf -from cuopt import routing - -# Cost matrix (n_locations x n_locations) -cost_matrix = cudf.DataFrame([ - [0, 10, 15, 20], - [10, 0, 12, 18], - [15, 12, 0, 10], - [20, 18, 10, 0], -], dtype="float32") - -# Build data model -dm = routing.DataModel( - n_locations=4, # Total locations including depot - n_fleet=2, # Number of vehicles - n_orders=3 # Orders to fulfill (locations 1,2,3) -) - -# Required: cost matrix -dm.add_cost_matrix(cost_matrix) - -# Required: order locations (which location each order is at) -dm.set_order_locations(cudf.Series([1, 2, 3])) - -# Solve -solution = routing.Solve(dm, routing.SolverSettings()) - -# Check result -if solution.get_status() == 0: # SUCCESS - solution.display_routes() -``` - -### Adding Constraints - -```python -# Time windows (need transit time matrix) -dm.add_transit_time_matrix(transit_time_matrix) -dm.set_order_time_windows( - cudf.Series([0, 10, 20]), # earliest - cudf.Series([50, 60, 70]) # latest -) - -# Capacities -dm.add_capacity_dimension( - "weight", - cudf.Series([20, 30, 25]), # demand per order - cudf.Series([100, 100]) # capacity per vehicle -) - -# Service times -dm.set_order_service_times(cudf.Series([5, 5, 5])) - -# Vehicle locations (start/end) -dm.set_vehicle_locations( - cudf.Series([0, 0]), # start at depot - cudf.Series([0, 0]) # return to depot -) - -# Vehicle time windows -dm.set_vehicle_time_windows( - cudf.Series([0, 0]), # earliest start - cudf.Series([200, 200]) # latest return -) -``` - -### Pickup and Delivery (PDP) - -```python -# Demand: positive=pickup, negative=delivery (must sum to 0 per pair) -demand = cudf.Series([10, -10, 15, -15]) - -# Pair indices: order 0 pairs with 1, order 2 pairs with 3 -dm.set_pickup_delivery_pairs( - cudf.Series([0, 2]), # pickup order indices - cudf.Series([1, 3]) # delivery order indices -) -``` - -### Precedence Constraints - -Use `add_order_precedence()` to require certain orders to be visited before others. - -**Important:** This is a per-node API — call it once for each order that has predecessors. - -```python -import numpy as np - -# Order 2 must come after orders 0 and 1 -dm.add_order_precedence( - node_id=2, # this order - preceding_nodes=np.array([0, 1]) # must come after these -) - -# Order 3 must come after order 2 -dm.add_order_precedence( - node_id=3, - preceding_nodes=np.array([2]) -) -``` - -**Rules:** -- Call once per order that has predecessors -- `preceding_nodes` is a numpy array of order indices -- Circular dependencies are NOT allowed (A before B before A) -- Orders without precedence constraints don't need a call - -**Example: Assembly sequence** -```python -# Task B requires Task A to be done first -# Task C requires Tasks A and B to be done first -dm.add_order_precedence(1, np.array([0])) # B after A -dm.add_order_precedence(2, np.array([0, 1])) # C after A and B -``` - -## Quick Reference: REST Server - -### Terminology Difference - -| Concept | Python API | REST Server | -|---------|------------|-------------| -| Jobs | `order_locations` | `task_locations` | -| Time windows | `set_order_time_windows()` | `task_time_windows` | -| Service times | `set_order_service_times()` | `service_times` | - -### Minimal REST Payload - -```json -{ - "cost_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "task_data": { - "task_locations": [1, 2] - }, - "fleet_data": { - "vehicle_locations": [[0, 0]], - "capacities": [[100]] - }, - "solver_config": { - "time_limit": 10 - } -} -``` - -## Solution Checking - -```python -status = solution.get_status() -# 0 = SUCCESS -# 1 = FAIL -# 2 = TIMEOUT -# 3 = EMPTY - -if status == 0: - solution.display_routes() - route_df = solution.get_route() - total_cost = solution.get_total_objective() -else: - print(f"Error: {solution.get_error_message()}") - infeasible = solution.get_infeasible_orders() - if len(infeasible) > 0: - print(f"Infeasible orders: {infeasible.to_list()}") -``` - -## Solution DataFrame Schema - -`solution.get_route()` returns a `cudf.DataFrame` with these columns: - -| Column | Type | Description | -|--------|------|-------------| -| `route` | int | Order/task index in the route sequence | -| `truck_id` | int | Vehicle ID assigned to this stop | -| `location` | int | Location index (0 = depot typically) | -| `arrival_stamp` | float | Arrival time at this location | - -**Example output:** -``` - route arrival_stamp truck_id location -0 0 0.0 1 0 # Vehicle 1 starts at depot -1 3 2.0 1 3 # Vehicle 1 visits location 3 -2 2 4.0 1 2 # Vehicle 1 visits location 2 -3 0 5.0 1 0 # Vehicle 1 returns to depot -4 0 0.0 0 0 # Vehicle 0 starts at depot -5 1 1.0 0 1 # Vehicle 0 visits location 1 -6 0 3.0 0 0 # Vehicle 0 returns to depot -``` - -**Working with results:** -```python -route_df = solution.get_route() - -# Routes per vehicle -for vid in route_df["truck_id"].unique().to_arrow().tolist(): - vehicle_route = route_df[route_df["truck_id"] == vid] - locations = vehicle_route["location"].to_arrow().tolist() - print(f"Vehicle {vid}: {locations}") - -# Total travel time -max_arrival = route_df["arrival_stamp"].max() -``` - -## Common Issues - -| Problem | Likely Cause | Fix | -|---------|--------------|-----| -| Empty solution | Time windows too tight | Widen windows or check travel times | -| Infeasible orders | Demand > capacity | Increase fleet or capacity | -| Status != 0 | Missing transit time matrix | Add `add_transit_time_matrix()` when using time windows | -| Wrong route cost | Matrix not symmetric | Check cost_matrix values | - -## Data Type Requirements - -```python -# Always use explicit dtypes -cost_matrix = cost_matrix.astype("float32") -order_locations = cudf.Series([...], dtype="int32") -demand = cudf.Series([...], dtype="int32") -vehicle_capacity = cudf.Series([...], dtype="int32") -time_windows = cudf.Series([...], dtype="int32") -``` - -## Solver Settings - -```python -ss = routing.SolverSettings() -ss.set_time_limit(30) # seconds -ss.set_verbose_mode(True) # enable progress output -ss.set_error_logging_mode(True) # log constraint errors if infeasible -``` - -## Examples - -See `resources/` for complete examples: -- [Python API](resources/python_examples.md) — VRP, PDP, multi-depot -- [REST Server](resources/server_examples.md) — curl and Python requests - -## When to Escalate - -Switch to **cuopt-debugging** if: -- Solution is infeasible and you can't determine why -- Performance is unexpectedly slow - -Switch to **cuopt-developer** if: -- User wants to modify solver behavior -- User wants to add new constraint types diff --git a/.github/skills/cuopt-server-api-python/SKILL.md b/.github/skills/cuopt-server-api-python/SKILL.md new file mode 100644 index 0000000000..2fb4558817 --- /dev/null +++ b/.github/skills/cuopt-server-api-python/SKILL.md @@ -0,0 +1,74 @@ +--- +name: cuopt-server-api-python +description: cuOpt REST server — start server, endpoints, Python/curl client examples. Use with cuopt-server-common for capabilities and workflow. Use when the user is deploying or calling the REST API. +--- + +# cuOpt Server — Deploy and client (Python/curl) + +**Concepts:** Read `cuopt-server-common/SKILL.md` for capabilities, workflow, when to escalate. + +This skill covers **starting the server** and **client examples** (curl, Python). Server has no separate C API (clients can be any language). + +## Start server + +```bash +# Development +python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 + +# Docker +docker run --gpus all -d -p 8000:8000 -e CUOPT_SERVER_PORT=8000 \ + nvidia/cuopt:latest-cuda12.9-py3.13 +``` + +## Verify + +```bash +curl http://localhost:8000/cuopt/health +``` + +## Workflow + +1. POST to `/cuopt/request` → get `reqId` +2. Poll `/cuopt/solution/{reqId}` until solution ready +3. Parse response + +## Python client (routing) + +```python +import requests, time +SERVER = "http://localhost:8000" +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} +payload = { + "cost_matrix_data": {"data": {"0": [[0,10,15],[10,0,12],[15,12,0]]}}, + "travel_time_matrix_data": {"data": {"0": [[0,10,15],[10,0,12],[15,12,0]]}}, + "task_data": {"task_locations": [1, 2], "demand": [[10, 20]], "task_time_windows": [[0,100],[0,100]], "service_times": [5, 5]}, + "fleet_data": {"vehicle_locations": [[0, 0]], "capacities": [[50]], "vehicle_time_windows": [[0, 200]]}, + "solver_config": {"time_limit": 5} +} +r = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) +req_id = r.json()["reqId"] +# Poll: GET /cuopt/solution/{req_id} +``` + +## Terminology: REST vs Python API + +| Python API | REST | +|------------|------| +| order_locations | task_locations | +| set_order_time_windows() | task_time_windows | +| service_times | service_times | + +Use `travel_time_matrix_data` (not transit_time_matrix_data). Capacities: `[[50, 50]]` not `[[50], [50]]`. + +## Debugging (422 / payload) + +**Validation errors:** Check field names against OpenAPI (`/cuopt.yaml`). Common mistakes: `transit_time_matrix_data` → `travel_time_matrix_data`; capacities per dimension `[[50, 50]]` not per vehicle `[[50], [50]]`. Capture `reqId` and response body for failed requests. + +## Examples + +- [routing_examples.md](resources/routing_examples.md) — VRP/PDP via REST +- [lp_milp_examples.md](resources/lp_milp_examples.md) — LP/MILP via REST + +## Escalate + +See `cuopt-server-common` for when to use cuopt-developer. diff --git a/.github/skills/cuopt-server/resources/lp_milp_examples.md b/.github/skills/cuopt-server-api-python/resources/lp_milp_examples.md similarity index 100% rename from .github/skills/cuopt-server/resources/lp_milp_examples.md rename to .github/skills/cuopt-server-api-python/resources/lp_milp_examples.md diff --git a/.github/skills/cuopt-server/resources/routing_examples.md b/.github/skills/cuopt-server-api-python/resources/routing_examples.md similarity index 100% rename from .github/skills/cuopt-server/resources/routing_examples.md rename to .github/skills/cuopt-server-api-python/resources/routing_examples.md diff --git a/.github/skills/cuopt-server-common/SKILL.md b/.github/skills/cuopt-server-common/SKILL.md new file mode 100644 index 0000000000..01cd9779b1 --- /dev/null +++ b/.github/skills/cuopt-server-common/SKILL.md @@ -0,0 +1,45 @@ +--- +name: cuopt-server-common +description: cuOpt REST server — what it does and how requests flow. Domain concepts; no deploy or client code. +--- + +# cuOpt Server (common) + +Domain concepts for the cuOpt REST server. No deploy commands or client code here. + +## What the server does + +- Accepts optimization requests (routing, LP, MILP) over HTTP. +- Returns a request ID; solution is obtained by polling with that ID. +- Does **not** support QP via REST. + +## Problem types supported + +| Problem type | Supported | +|--------------|:---------:| +| Routing | ✓ | +| LP | ✓ | +| MILP | ✓ | +| QP | ✗ | + +## Request flow (conceptual) + +1. Client sends problem data in the required schema (matrices, tasks, fleet, solver config). +2. Server returns a `reqId`. +3. Client polls the solution endpoint with `reqId` until the job completes. +4. Response contains status and, on success, solution (routes, objective, primal values, etc.). + +## Required questions (deployment and usage) + +Ask these if not already clear: + +1. **Problem type** — Routing or LP/MILP? (QP not available.) +2. **Deployment** — Local, Docker, Kubernetes, or cloud? +3. **Client** — Which language or tool will call the API (e.g. Python, curl, another service)? + +## Key endpoints (conceptual) + +- Health check. +- Submit request (POST). +- Get solution by request ID (GET). +- OpenAPI spec (e.g. for payload format). diff --git a/.github/skills/cuopt-server/SKILL.md b/.github/skills/cuopt-server/SKILL.md deleted file mode 100644 index e118a0ba43..0000000000 --- a/.github/skills/cuopt-server/SKILL.md +++ /dev/null @@ -1,356 +0,0 @@ ---- -name: cuopt-server -description: Deploy and integrate cuOpt REST server for production use. Use when the user asks about REST API, HTTP endpoints, deployment, curl requests, microservices, async solving, or server payloads. ---- - -# cuOpt Server Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Deploy and use the cuOpt REST server for production optimization workloads. - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **Problem type?** - - Routing (VRP/TSP/PDP)? - - LP/MILP? - - (Note: QP not supported via REST) - -2. **Deployment target?** - - Local development? - - Docker/Kubernetes? - - Cloud service? - -3. **Client preference?** - - curl (quick testing) - - Python requests - - cuopt-sh-client library - -## Server Capabilities - -| Problem Type | REST Support | -|--------------|:------------:| -| Routing | ✓ | -| LP | ✓ | -| MILP | ✓ | -| QP | ✗ | - -## Starting the Server - -### Direct (Development) - -```bash -python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 -``` - -### Docker (Production) - -```bash -docker run --gpus all -d \ - -p 8000:8000 \ - -e CUOPT_SERVER_PORT=8000 \ - --name cuopt-server \ - nvidia/cuopt:latest-cuda12.9-py3.13 -``` - -### Verify Running - -```bash -curl http://localhost:8000/cuopt/health -# Expected: {"status": "healthy"} -``` - -## API Endpoints - -| Endpoint | Method | Purpose | -|----------|--------|---------| -| `/cuopt/health` | GET | Health check | -| `/cuopt/request` | POST | Submit optimization request | -| `/cuopt/solution/{reqId}` | GET | Get solution by request ID | -| `/cuopt.yaml` | GET | OpenAPI specification | -| `/cuopt/docs` | GET | Swagger UI | - -## Workflow - -1. **POST** problem to `/cuopt/request` → get `reqId` -2. **Poll** `/cuopt/solution/{reqId}` until solution ready -3. **Parse** response - -## Routing Request Example - -### curl - -```bash -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "cost_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "task_data": { - "task_locations": [1, 2], - "demand": [[10, 20]], - "task_time_windows": [[0, 100], [0, 100]], - "service_times": [5, 5] - }, - "fleet_data": { - "vehicle_locations": [[0, 0]], - "capacities": [[50]], - "vehicle_time_windows": [[0, 200]] - }, - "solver_config": {"time_limit": 5} - }' | jq -r '.reqId') - -echo "Request ID: $REQID" - -# Poll for solution -sleep 2 -curl -s "http://localhost:8000/cuopt/solution/$REQID" \ - -H "CLIENT-VERSION: custom" | jq . -``` - -### Python - -```python -import requests -import time - -SERVER = "http://localhost:8000" -HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} - -payload = { - "cost_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "task_data": { - "task_locations": [1, 2], - "demand": [[10, 20]], - "task_time_windows": [[0, 100], [0, 100]], # optional - "service_times": [5, 5] # optional - }, - "fleet_data": { - "vehicle_locations": [[0, 0]], - "capacities": [[50]], - "vehicle_time_windows": [[0, 200]] # optional - }, - "solver_config": {"time_limit": 5} -} - -# Submit -resp = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) -req_id = resp.json()["reqId"] - -# Poll -for _ in range(30): - resp = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) - result = resp.json() - if "response" in result: - print(result["response"]["solver_response"]) - break - time.sleep(1) -``` - -## LP/MILP Request Example - -```bash -curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": true, - "solver_config": {"time_limit": 60} - }' -``` - -## Terminology: REST vs Python API - -**CRITICAL:** REST API uses different terminology than Python API. - -| Concept | Python API | REST API | -|---------|------------|----------| -| Orders/Jobs | `order_locations` | `task_locations` | -| Time windows | `set_order_time_windows()` | `task_time_windows` | -| Service times | `set_order_service_times()` | `service_times` | -| Transit matrix | `add_transit_time_matrix()` | `travel_time_matrix_data` | - -## Common Payload Mistakes - -### Wrong field names - -```json -// ❌ WRONG -"transit_time_matrix_data": {...} - -// ✅ CORRECT -"travel_time_matrix_data": {...} -``` - -### Wrong capacity format - -```json -// ❌ WRONG - per vehicle -"capacities": [[50], [50]] - -// ✅ CORRECT - per dimension across all vehicles -"capacities": [[50, 50]] -``` - -### Missing required fields - -Routing requires at minimum: -- `cost_matrix_data` -- `task_data.task_locations` -- `fleet_data.vehicle_locations` -- `fleet_data.capacities` - -## Response Structure - -### Routing Success - -```json -{ - "reqId": "abc123", - "response": { - "solver_response": { - "status": 0, - "solution_cost": 45.0, - "vehicle_data": { - "0": {"route": [0, 1, 2, 0], "arrival_times": [...]} - } - } - } -} -``` - -### LP/MILP Success - -```json -{ - "reqId": "abc123", - "response": { - "status": "Optimal", - "objective_value": 1600.0, - "primal_solution": [30.0, 60.0] - } -} -``` - -## Error Handling - -### 422 Validation Error - -Check the error message for field issues: -```bash -curl ... | jq '.error' -``` - -Compare against OpenAPI spec at `/cuopt.yaml` - -### 500 Server Error - -- Check server logs -- Capture `reqId` for debugging -- Try with smaller problem - -### Polling Returns Empty - -- Solution still computing - keep polling -- Check `solver_config.time_limit` - -## Server Configuration - -### Environment Variables - -```bash -CUOPT_SERVER_PORT=8000 -CUOPT_SERVER_HOST=0.0.0.0 -``` - -### Command Line Options - -```bash -python -m cuopt_server.cuopt_service \ - --ip 0.0.0.0 \ - --port 8000 \ - --workers 4 -``` - -## Production Considerations - -### Health Checks - -```bash -# Kubernetes liveness probe -curl -f http://localhost:8000/cuopt/health - -# Readiness check -curl -f http://localhost:8000/cuopt/health -``` - -### Resource Limits - -```yaml -# Kubernetes example -resources: - limits: - nvidia.com/gpu: 1 - memory: "32Gi" - requests: - memory: "16Gi" -``` - -### Scaling - -- GPU is the bottleneck - one server per GPU -- Use load balancer for multiple GPUs -- Queue requests to avoid overwhelming - -## OpenAPI Specification - -Full API spec available at: -- Runtime: `http://localhost:8000/cuopt.yaml` -- Source: `docs/cuopt/source/cuopt_spec.yaml` -- Swagger UI: `http://localhost:8000/cuopt/docs` - -## Examples - -See `resources/` for complete examples: -- [Routing examples](resources/routing_examples.md) — VRP, PDP via REST -- [LP/MILP examples](resources/lp_milp_examples.md) — Linear programming via REST - -## When to Escalate - -Switch to **cuopt-debugging** if: -- Consistent 5xx errors -- Unexpected solution results - -Switch to **cuopt-developer** if: -- Need to modify server behavior -- Need new endpoints diff --git a/.github/skills/cuopt-user-rules/SKILL.md b/.github/skills/cuopt-user-rules/SKILL.md index a6c870c10d..53169227b3 100644 --- a/.github/skills/cuopt-user-rules/SKILL.md +++ b/.github/skills/cuopt-user-rules/SKILL.md @@ -1,6 +1,6 @@ --- name: cuopt-user-rules -description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before any cuOpt user task (routing, LP/MILP, QP, debugging, installation, server). Covers handling incomplete questions, clarifying data requirements, verifying understanding, and running commands safely. +description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before any cuOpt user task (routing, LP/MILP, QP, installation, server). Covers handling incomplete questions, clarifying data requirements, verifying understanding, and running commands safely. --- # cuOpt User Rules @@ -13,7 +13,7 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before **Always clarify ambiguous requirements before implementing:** -- What interface? (Python API / REST Server / C API / CLI) +- What **language/interface**? (**Python** API / **C** API / REST Server / CLI) - What problem type? (Routing / LP / MILP / QP) - What constraints matter? (time windows, capacities, etc.) - What output format? (solution values, routes, visualization) @@ -95,17 +95,16 @@ Is this correct?" ## 6. Read Examples First -Before generating code, **read the canonical example** for that problem type: +Before generating code, **read the canonical example** for that problem type and **language** (Python vs C): -| Problem | Example Location | -|---------|------------------| -| Routing | `docs/cuopt/source/cuopt-python/routing/examples/` | -| LP/MILP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/` | -| QP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_qp_example.py` | -| Server | `docs/cuopt/source/cuopt_spec.yaml` (OpenAPI) | -| C API | `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/` | +| Problem | Python | C | +|---------|--------|---| +| Routing | `docs/cuopt/source/cuopt-python/routing/examples/` | — (no C API) | +| LP/MILP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/` | `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/` | +| QP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_qp_example.py` | `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/` | +| Server | `docs/cuopt/source/cuopt_spec.yaml` (OpenAPI) | — | -**Don't invent API patterns.** Copy from examples. +**Don't invent API patterns.** Copy from examples. For C, use the skill’s `resources/c_api_examples.md` when available. With the flat layout, use the api skill for the user's language (e.g. cuopt-lp-milp-api-python or cuopt-lp-milp-api-c); each has resources/examples.md or equivalent. --- @@ -129,16 +128,16 @@ Provide diagnostic code snippets when helpful. - "Do you have cuOpt installed? If so, which interface?" - "What environment are you using? (local GPU, cloud, Docker, server, etc.)" -2. **Different packages for different interfaces:** +2. **Different packages by language/interface:** - | Interface | Package | Check | - |-----------|---------|-------| - | Python API | `cuopt` (pip/conda) | `import cuopt` | - | C API | `libcuopt` (conda/system) | `find libcuopt.so` or header check | + | Language / Interface | Package | Check | + |----------------------|---------|-------| + | **Python** | `cuopt` (pip/conda) | `import cuopt` | + | **C** | `libcuopt` (conda/system) | `find libcuopt.so` or header check | | REST Server | `cuopt-server` or Docker | `curl /cuopt/health` | | CLI | `cuopt` package includes CLI | `cuopt_cli --help` | - **Note:** `libcuopt` (C library) installed via conda is NOT available through Python import — they are separate packages. + **Note:** `libcuopt` (C library) is separate from the Python package — C and Python use different installs. 3. **If not installed, ask how they want to access:** - "Would you like help installing cuOpt, or do you have access another way?" From dde249a11f6c5d535cbd65a4bb64b52de9286725 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 27 Feb 2026 12:56:46 -0600 Subject: [PATCH 02/35] update --- .github/AGENTS.md | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/.github/AGENTS.md b/.github/AGENTS.md index 7854e6599f..e6ad330723 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -1,28 +1,32 @@ # AGENTS.md - cuOpt AI Agent Entry Point -AI agent skills for NVIDIA cuOpt optimization engine. +AI agent skills for NVIDIA cuOpt optimization engine. Skills use a **flat layout**: **common** (concepts) + **api-python** or **api-c** (implementation) per domain. ## Quick Start -| Task | Read These Skills | -|------|-------------------| -| **Using cuOpt** (routing, LP, etc.) | `skills/cuopt-user-rules/` → then domain skill | -| **Developing cuOpt** (contributing) | `skills/cuopt-developer/` | +- **Using cuOpt** (routing, LP, QP, install, server): read `skills/cuopt-user-rules/`, then choose skills from the index below based on the user’s task, problem type, and interface (Python / C / CLI). +- **Developing cuOpt** (contributing): read `skills/cuopt-developer/`. -## Skills Directory +Choose which skills to load from the index; -See `skills/README.md` for the full index. +## Skills directory (flat) -### User Skills (read cuopt-user-rules first) -- `skills/cuopt-routing/` — VRP, TSP, PDP -- `skills/cuopt-lp-milp/` — Linear programming, integer variables -- `skills/cuopt-qp/` — Quadratic programming -- `skills/cuopt-debugging/` — Troubleshooting -- `skills/cuopt-installation/` — Setup & requirements -- `skills/cuopt-server/` — REST API deployment +### Rules +- `skills/cuopt-user-rules/` — Behavior rules (read first for user tasks) +- `skills/cuopt-developer/` — Contributing (own rules) -### Developer Skill (has its own rules) -- `skills/cuopt-developer/` — Contributing code +### Common (concepts only; no API code) +- `skills/cuopt-lp-milp-common/` — LP/MILP: when to use, escalate +- `skills/cuopt-routing-common/` — Routing: VRP, TSP, PDP +- `skills/cuopt-qp-common/` — QP: minimize-only, escalate +- `skills/cuopt-server-common/` — Server: capabilities, workflow + +### API (implementation; one interface per skill) +- `skills/cuopt-installation-api-python/`, `skills/cuopt-installation-api-c/` (user), `skills/cuopt-installation-developer/` (build from source; no common) +- `skills/cuopt-lp-milp-api-python/`, `skills/cuopt-lp-milp-api-c/`, `skills/cuopt-lp-milp-api-cli/` +- `skills/cuopt-routing-api-python/` (no C for routing) +- `skills/cuopt-qp-api-python/`, `skills/cuopt-qp-api-c/`, `skills/cuopt-qp-api-cli/` +- `skills/cuopt-server-api-python/` (deploy + client) ## Resources From de996c43ace9877f8c03c92fb2b2916a8e69b0a5 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 27 Feb 2026 13:05:58 -0600 Subject: [PATCH 03/35] add support for claude cursor and opencode --- .claude/AGENTS.md | 1 + .claude/CLAUDE.md | 1 + .claude/skills | 1 + .cursor/AGENTS.md | 1 + .cursor/skills | 1 + .opencode/AGENTS.md | 1 + .opencode/skills | 1 + 7 files changed, 7 insertions(+) create mode 120000 .claude/AGENTS.md create mode 120000 .claude/CLAUDE.md create mode 120000 .claude/skills create mode 120000 .cursor/AGENTS.md create mode 120000 .cursor/skills create mode 120000 .opencode/AGENTS.md create mode 120000 .opencode/skills diff --git a/.claude/AGENTS.md b/.claude/AGENTS.md new file mode 120000 index 0000000000..f5f4bd7b93 --- /dev/null +++ b/.claude/AGENTS.md @@ -0,0 +1 @@ +../.github/AGENTS.md \ No newline at end of file diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 120000 index 0000000000..f5f4bd7b93 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +../.github/AGENTS.md \ No newline at end of file diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 0000000000..3e73f3a383 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.github/skills \ No newline at end of file diff --git a/.cursor/AGENTS.md b/.cursor/AGENTS.md new file mode 120000 index 0000000000..f5f4bd7b93 --- /dev/null +++ b/.cursor/AGENTS.md @@ -0,0 +1 @@ +../.github/AGENTS.md \ No newline at end of file diff --git a/.cursor/skills b/.cursor/skills new file mode 120000 index 0000000000..3e73f3a383 --- /dev/null +++ b/.cursor/skills @@ -0,0 +1 @@ +../.github/skills \ No newline at end of file diff --git a/.opencode/AGENTS.md b/.opencode/AGENTS.md new file mode 120000 index 0000000000..f5f4bd7b93 --- /dev/null +++ b/.opencode/AGENTS.md @@ -0,0 +1 @@ +../.github/AGENTS.md \ No newline at end of file diff --git a/.opencode/skills b/.opencode/skills new file mode 120000 index 0000000000..3e73f3a383 --- /dev/null +++ b/.opencode/skills @@ -0,0 +1 @@ +../.github/skills \ No newline at end of file From 87f7d0d237d2f8e0b65d0b596005b14005428c30 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 27 Feb 2026 13:09:32 -0600 Subject: [PATCH 04/35] update cuopt-lp-milp-api-python --- .../skills/cuopt-lp-milp-api-python/SKILL.md | 187 ++++++++++--- .../cuopt-lp-milp-api-python/assets/README.md | 12 + .../assets/lp_basic/README.md | 7 + .../assets/lp_basic/model.py | 36 +++ .../assets/lp_duals/README.md | 7 + .../assets/lp_duals/model.py | 36 +++ .../assets/lp_warmstart/README.md | 5 + .../assets/lp_warmstart/model.py | 52 ++++ .../assets/milp_basic/README.md | 10 + .../assets/milp_basic/incumbent_callback.py | 44 +++ .../assets/milp_basic/model.py | 36 +++ .../assets/milp_production_planning/README.md | 5 + .../assets/milp_production_planning/model.py | 33 +++ .../assets/mps_solver/README.md | 88 ++++++ .../assets/mps_solver/data/README.md | 82 ++++++ .../assets/mps_solver/data/sample.mps | 19 ++ .../assets/mps_solver/model.py | 238 ++++++++++++++++ .../assets/mps_solver/results.md | 90 ++++++ .../resources/examples.md | 257 ------------------ .../resources/server_examples.md | 208 -------------- 20 files changed, 948 insertions(+), 504 deletions(-) create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py create mode 100644 .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md delete mode 100644 .github/skills/cuopt-lp-milp-api-python/resources/examples.md delete mode 100644 .github/skills/cuopt-lp-milp-api-python/resources/server_examples.md diff --git a/.github/skills/cuopt-lp-milp-api-python/SKILL.md b/.github/skills/cuopt-lp-milp-api-python/SKILL.md index 1d836ea84b..717be801da 100644 --- a/.github/skills/cuopt-lp-milp-api-python/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-python/SKILL.md @@ -1,13 +1,38 @@ --- name: cuopt-lp-milp-api-python -description: LP and MILP with cuOpt — Python API only. Use with cuopt-lp-milp-common for concepts. Use when the user is building or solving LP/MILP in Python. +description: Solve Linear Programming (LP) and Mixed-Integer Linear Programming (MILP) with the Python API. Use when the user asks about optimization with linear constraints, integer variables, scheduling, resource allocation, facility location, or production planning. --- -# cuOpt LP/MILP — Python API +# cuOpt LP/MILP Skill -**Concepts:** Read `cuopt-lp-milp-common/SKILL.md` for problem type, when to use, when to escalate. +Model and solve linear and mixed-integer linear programs using NVIDIA cuOpt's GPU-accelerated solver. -This skill is **Python only**. For C, use `cuopt-lp-milp-api-c`. +## Before You Start + +Use the summary from **problem-statement-parsing** (parameters, constraints, decisions, objective) if available; otherwise ask for decision variables, objective, and constraints. Then confirm **variable types** (see below) and **interface** (Python API recommended). + +## Choosing LP vs MILP + +**Prefer LP (all continuous variables) when the problem allows it.** LP solves faster and has stronger optimality guarantees. Use **MILP** only when the problem logically requires whole numbers or yes/no decisions. + +**Problem types that need extra care:** Multi-period planning and goal programming are easy to misinterpret. Double-check that rates and constraints apply to the right time period or priority level (AGENTS.md: verify understanding before code). + +- **Use LP** when every quantity can meaningfully be fractional: flows, proportions, rates, dollars, hours, tonnes of material, etc. +- **Use MILP** when the problem mentions **counts** of discrete entities, **yes/no** choices, or **either/or** decisions (e.g. open a facility or not, assign a person to a shift, number of trucks). + +## Integer vs continuous from wording + +Choose variable type from what the problem describes. + +| Problem wording / concept | Variable type | Examples | +|---------------------------|---------------|----------| +| **Discrete entities (counts)** | **INTEGER** | Workers, cars, trucks, machines, pilots, facilities, units to manufacture (when "units" means whole items), trainees, vehicles | +| **Yes/no or on/off** | **INTEGER** (binary, lb=0 ub=1) | Open a facility, run a machine, produce a product line, assign a person to a shift | +| **Amounts that can be fractional** | **CONTINUOUS** | Tonnes, litres, dollars, hours, kWh, proportion of capacity, flow volume, weight | +| **Rates or fractions** | **CONTINUOUS** | Utilization, percentage, share of budget | +| **Unclear** | Prefer **INTEGER** if the noun is a countable thing (a worker, a car); prefer **CONTINUOUS** if it's a measure (amount of steel, hours worked). If the problem says "whole" or "integer" or "number of", use INTEGER. | + +**Rule of thumb:** If the quantity is "how many *things*" (people, vehicles, items, sites), use **INTEGER**. If it's "how much" (mass, volume, money, time) or a rate, use **CONTINUOUS** unless the problem explicitly requires whole numbers. ## Quick Reference: Python API @@ -17,100 +42,184 @@ This skill is **Python only**. For C, use `cuopt-lp-milp-api-c`. from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE from cuopt.linear_programming.solver_settings import SolverSettings +# Create problem problem = Problem("MyLP") + +# Decision variables x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + +# Constraints problem.addConstraint(2*x + 3*y <= 120, name="resource_a") problem.addConstraint(4*x + 2*y <= 100, name="resource_b") + +# Objective problem.setObjective(40*x + 30*y, sense=MAXIMIZE) + +# Solve settings = SolverSettings() settings.set_parameter("time_limit", 60) problem.solve(settings) +# Check status (CRITICAL: use PascalCase!) if problem.Status.name in ["Optimal", "PrimalFeasible"]: print(f"Objective: {problem.ObjValue}") - print(f"x = {x.getValue()}, y = {y.getValue()}") + print(f"x = {x.getValue()}") + print(f"y = {y.getValue()}") ``` -### MILP Example (integer variables) +### MILP Example (with integer variables) ```python from cuopt.linear_programming.problem import Problem, CONTINUOUS, INTEGER, MINIMIZE problem = Problem("FacilityLocation") + +# Binary variable (integer with bounds 0-1) open_facility = problem.addVariable(lb=0, ub=1, vtype=INTEGER, name="open") + +# Continuous variable production = problem.addVariable(lb=0, vtype=CONTINUOUS, name="production") + +# Linking constraint: can only produce if facility is open problem.addConstraint(production <= 1000 * open_facility, name="link") + +# Objective: fixed cost + variable cost problem.setObjective(500*open_facility + 2*production, sense=MINIMIZE) + +# MILP-specific settings settings = SolverSettings() settings.set_parameter("time_limit", 120) -settings.set_parameter("mip_relative_gap", 0.01) +settings.set_parameter("mip_relative_gap", 0.01) # 1% optimality gap + problem.solve(settings) +# Check status if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(f"Open: {open_facility.getValue() > 0.5}, Production: {production.getValue()}") + print(f"Open facility: {open_facility.getValue() > 0.5}") + print(f"Production: {production.getValue()}") ``` -## CRITICAL: Status Checking (PascalCase) +## CRITICAL: Status Checking + +**Status values use PascalCase, NOT ALL_CAPS:** ```python # ✅ CORRECT if problem.Status.name in ["Optimal", "FeasibleFound"]: print(problem.ObjValue) -# ❌ WRONG — never matches -if problem.Status.name == "OPTIMAL": +# ❌ WRONG - will silently fail! +if problem.Status.name == "OPTIMAL": # Never matches! print(problem.ObjValue) ``` -**LP status:** `Optimal`, `PrimalFeasible`, `PrimalInfeasible`, `TimeLimit`, etc. -**MILP status:** `Optimal`, `FeasibleFound`, `Infeasible`, `TimeLimit`, etc. +**LP Status Values:** `Optimal`, `NoTermination`, `NumericalError`, `PrimalInfeasible`, `DualInfeasible`, `IterationLimit`, `TimeLimit`, `PrimalFeasible` + +**MILP Status Values:** `Optimal`, `FeasibleFound`, `Infeasible`, `Unbounded`, `TimeLimit`, `NoTermination` + +## Common Modeling Patterns + +### Binary Selection +```python +# Select exactly k items from n +items = [problem.addVariable(lb=0, ub=1, vtype=INTEGER) for _ in range(n)] +problem.addConstraint(sum(items) == k) +``` + +### Big-M Linking +```python +# If y=1, then x <= 100; if y=0, x can be anything up to M +M = 10000 +problem.addConstraint(x <= 100 + M*(1 - y)) +``` + +### If-then "must also produce" +When the problem says *if we do X then we must also do Y*, enforce both (i) the binary link and (ii) that Y is actually produced: +```python +# y_X <= y_Y (if we do X, we must "do" Y) +problem.addConstraint(y_X <= y_Y) +# Production of Y when Y is chosen: produce at least 1 (or a minimum) when y_Y=1 +problem.addConstraint(production_Y >= 1 * y_Y) # or min_amount * y_Y +``` +Otherwise the solver can set y_Y=1 but production_Y=0, satisfying the binary link but not the intent. + +### Building large expressions +Chained `+` over many terms can hit recursion limits in the API. Prefer building objectives and constraints with **LinearExpression**: +```python +from cuopt.linear_programming.problem import LinearExpression + +# Build as list of (vars, coeffs) instead of v1*c1 + v2*c2 + ... +vars_list = [x, y, z] +coeffs_list = [1.0, 2.0, 3.0] +expr = LinearExpression(vars_list, coeffs_list, constant=0.0) +problem.addConstraint(expr <= 100) +``` +See reference models in this skill's `assets/` for examples. + +### Piecewise Linear (SOS2) +```python +# Approximate nonlinear function with breakpoints +# Use lambda variables that sum to 1, at most 2 adjacent non-zero +``` ## Solver Settings ```python settings = SolverSettings() + +# Time limit settings.set_parameter("time_limit", 60) + +# MILP gap tolerance (stop when within X% of optimal) settings.set_parameter("mip_relative_gap", 0.01) + +# Logging settings.set_parameter("log_to_console", 1) ``` ## Common Issues -| Problem | Fix | -|---------|-----| -| Status never "Optimal" | Use `"Optimal"` (PascalCase), not `"OPTIMAL"` | -| Integer var fractional | Use `vtype=INTEGER` | -| Infeasible | Check constraint logic and bounds | -| Slow solve | Set time_limit, mip_relative_gap | +| Problem | Likely Cause | Fix | +|---------|--------------|-----| +| Status never "OPTIMAL" | Using wrong case | Use `"Optimal"` not `"OPTIMAL"` | +| Integer var has fractional value | Defined as CONTINUOUS | Use `vtype=INTEGER` | +| Infeasible | Conflicting constraints | Check constraint logic | +| Unbounded | Missing bounds | Add variable bounds | +| Slow solve | Large problem | Set time limit, increase gap tolerance | +| Maximum recursion depth | Building big expr with chained `+` | Use `LinearExpression(vars_list, coeffs_list, constant)` | -## Debugging +## Getting Dual Values (LP only) -**Diagnostic — status:** `print(f"Actual status: '{problem.Status.name}'")` - -**Infeasible inspection:** List constraints and check for conflicts: ```python -if problem.Status.name == "Infeasible": - for name in constraint_names: - c = problem.getConstraint(name) - print(f"{name}: {c}") +if problem.Status.name == "Optimal": + constraint = problem.getConstraint("resource_a") + shadow_price = constraint.DualValue + print(f"Shadow price: {shadow_price}") ``` -**Wrong objective:** Print variable values: `print(f"{var.name}: {var.getValue()}")` and `problem.ObjValue`. +## Reference Models -## Dual Values (LP only) +All reference models live in this skill's **`assets/`** directory. Use them as reference when building new applications; do not edit them in place. -```python -if problem.Status.name == "Optimal": - c = problem.getConstraint("resource_a") - print(f"Shadow price: {c.DualValue}") -``` +### Minimal / canonical examples (LP & MILP) +| Model | Type | Description | +|-------|------|-------------| +| [lp_basic](assets/lp_basic/) | LP | Minimal LP: variables, constraints, objective, solve | +| [lp_duals](assets/lp_duals/) | LP | Dual values and reduced costs | +| [lp_warmstart](assets/lp_warmstart/) | LP | PDLP warmstart for similar problems | +| [milp_basic](assets/milp_basic/) | MILP | Minimal MIP; includes incumbent callback example | +| [milp_production_planning](assets/milp_production_planning/) | MILP | Production planning with resource constraints | -## Examples +### Other reference +| Model | Type | Description | +|-------|------|-------------| +| [mps_solver](assets/mps_solver/) | LP/MILP | Solve any problem from standard MPS file format | -- [examples.md](resources/examples.md) — LP, MILP, knapsack, transportation -- [server_examples.md](resources/server_examples.md) — REST from Python +**Quick command to list models:** `ls assets/` (from this skill's directory). -## Escalate +## When to Escalate -See `cuopt-lp-milp-common` for when to use cuopt-qp-common or cuopt-developer. +Use troubleshooting and diagnostic guidance if: +- Infeasible and you can't determine why +- Numerical issues diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/README.md new file mode 100644 index 0000000000..0b9a727e4b --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/README.md @@ -0,0 +1,12 @@ +# Assets — reference models + +LP/MILP reference implementations. Use as reference when building new applications; do not edit in place. + +| Model | Type | +|-------|------| +| lp_basic | LP | +| lp_duals | LP | +| lp_warmstart | LP | +| milp_basic | MILP | +| milp_production_planning | MILP | +| mps_solver | LP/MILP | diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md new file mode 100644 index 0000000000..4c06f2ded6 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md @@ -0,0 +1,7 @@ +# Minimal LP + +Basic linear program: continuous variables, linear constraints, maximize objective. + +**Problem:** Maximize x + y subject to x + y ≤ 10, x − y ≥ 0, x, y ≥ 0. + +**Run:** `python model.py` diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py new file mode 100644 index 0000000000..d81c6a749d --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Minimal LP: variables, constraints, objective, solve. + +Problem: + Maximize: x + y + Subject to: x + y <= 10, x - y >= 0, x, y >= 0 +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + problem = Problem("Simple LP") + x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + problem.addConstraint(x + y <= 10, name="c1") + problem.addConstraint(x - y >= 0, name="c2") + problem.setObjective(x + y, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter("time_limit", 60) + problem.solve(settings) + + if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Objective: {problem.ObjValue}") + print(f"x = {x.getValue()}, y = {y.getValue()}") + else: + print(f"Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md new file mode 100644 index 0000000000..f0eb9bcf8b --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md @@ -0,0 +1,7 @@ +# LP Duals and Reduced Costs + +Retrieve dual values (shadow prices) and reduced costs after solving an LP. + +**Problem:** Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x, y, z ≥ 0. + +**Run:** `python model.py` diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py new file mode 100644 index 0000000000..ca7477583d --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +LP with dual values and reduced costs. + +Problem: + Minimize: 3x + 2y + 5z + Subject to: x + y + z = 4, 2x + y + z = 5, x, y, z >= 0 +""" + +from cuopt.linear_programming.problem import Problem, MINIMIZE + + +def main(): + problem = Problem("min_dual_rc") + x = problem.addVariable(lb=0.0, name="x") + y = problem.addVariable(lb=0.0, name="y") + z = problem.addVariable(lb=0.0, name="z") + problem.addConstraint(x + y + z == 4.0, name="c1") + problem.addConstraint(2.0 * x + y + z == 5.0, name="c2") + problem.setObjective(3.0 * x + 2.0 * y + 5.0 * z, sense=MINIMIZE) + problem.solve() + + if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Objective: {problem.ObjValue}") + for v in problem.getVariables(): + print(f"{v.VariableName} = {v.Value}, ReducedCost = {v.ReducedCost}") + for c in problem.getConstraints(): + print(f"{c.ConstraintName} DualValue = {c.DualValue}") + else: + print(f"Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md new file mode 100644 index 0000000000..000e7a42fa --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md @@ -0,0 +1,5 @@ +# LP PDLP Warmstart + +Use warmstart data from a solved LP to solve a similar problem faster. LP only (not MILP). + +**Run:** `python model.py` diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py new file mode 100644 index 0000000000..b0e893118f --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +PDLP warmstart: solve a similar LP faster by reusing solution context. + +Warmstart is for LP only, not MILP. +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver.solver_parameters import ( + CUOPT_METHOD, + CUOPT_PDLP_SOLVER_MODE, +) +from cuopt.linear_programming.solver_settings import ( + SolverSettings, + SolverMethod, + PDLPSolverMode, +) + + +def main(): + print("=== Problem 1 ===") + problem = Problem("LP1") + x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + problem.addConstraint(4 * x + 10 * y <= 130, name="c1") + problem.addConstraint(8 * x - 3 * y >= 40, name="c2") + problem.setObjective(2 * x + y, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter(CUOPT_METHOD, SolverMethod.PDLP) + settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Stable2) + problem.solve(settings) + print(f"Objective: {problem.ObjValue}") + + warmstart_data = problem.getWarmstartData() + print("\n=== Problem 2 (with warmstart) ===") + new_problem = Problem("LP2") + x = new_problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = new_problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + new_problem.addConstraint(4 * x + 10 * y <= 100, name="c1") + new_problem.addConstraint(8 * x - 3 * y >= 50, name="c2") + new_problem.setObjective(2 * x + y, sense=MAXIMIZE) + settings.set_pdlp_warm_start_data(warmstart_data) + new_problem.solve(settings) + if new_problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Objective: {new_problem.ObjValue}") + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md new file mode 100644 index 0000000000..45362da09b --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md @@ -0,0 +1,10 @@ +# Minimal MILP + +Basic mixed-integer program: integer variables with bounds, linear constraints. + +**Problem:** Maximize 5x + 3y subject to 2x + 4y ≥ 230, 3x + 2y ≤ 190, 10 ≤ y ≤ 50, x, y integer. + +- **model.py** — solve and print solution. +- **incumbent_callback.py** — same problem with a callback that prints intermediate (incumbent) solutions during solve. + +**Run:** `python model.py` or `python incumbent_callback.py` diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py new file mode 100644 index 0000000000..624734fc9d --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Same MILP as model.py but with a callback to receive incumbent (intermediate) solutions. +MILP only; not for LP. +""" + +from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings +from cuopt.linear_programming.solver.solver_parameters import CUOPT_TIME_LIMIT +from cuopt.linear_programming.internals import GetSolutionCallback + + +class IncumbentCallback(GetSolutionCallback): + def __init__(self): + super().__init__() + self.n_callbacks = 0 + + def get_solution(self, solution, solution_cost): + self.n_callbacks += 1 + sol = solution.copy_to_host() + cost = solution_cost.copy_to_host()[0] + print(f"Incumbent {self.n_callbacks}: {sol}, cost: {cost:.2f}") + + +def main(): + problem = Problem("Incumbent Example") + x = problem.addVariable(vtype=INTEGER) + y = problem.addVariable(vtype=INTEGER) + problem.addConstraint(2 * x + 4 * y >= 230) + problem.addConstraint(3 * x + 2 * y <= 190) + problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_mip_callback(IncumbentCallback()) + settings.set_parameter(CUOPT_TIME_LIMIT, 30) + problem.solve(settings) + + print(f"Status: {problem.Status.name}, Objective: {problem.ObjValue}") + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py new file mode 100644 index 0000000000..5c0bf88e15 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Minimal MILP: integer variables with bounds, linear constraints. + +Problem: + Maximize: 5x + 3y + Subject to: 2x + 4y >= 230, 3x + 2y <= 190, 10 <= y <= 50, x, y integer +""" + +from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + problem = Problem("Simple MIP") + x = problem.addVariable(vtype=INTEGER, name="V_x") + y = problem.addVariable(lb=10, ub=50, vtype=INTEGER, name="V_y") + problem.addConstraint(2 * x + 4 * y >= 230, name="C1") + problem.addConstraint(3 * x + 2 * y <= 190, name="C2") + problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter("time_limit", 60) + problem.solve(settings) + + if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(f"Objective: {problem.ObjValue}") + print(f"x = {x.getValue()}, y = {y.getValue()}") + else: + print(f"Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md new file mode 100644 index 0000000000..42a2a1a9d5 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md @@ -0,0 +1,5 @@ +# Production Planning (MILP) + +Two products (A, B), resource limits (machine time, labor, material), minimum production, maximize profit. + +**Run:** `python model.py` diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py new file mode 100644 index 0000000000..72ded8164d --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Production planning: two products, resource limits (machine, labor, material), maximize profit. +""" + +from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + problem = Problem("Production Planning") + x1 = problem.addVariable(lb=10, vtype=INTEGER, name="Product_A") + x2 = problem.addVariable(lb=15, vtype=INTEGER, name="Product_B") + problem.addConstraint(2 * x1 + x2 <= 100, name="Machine_Time") + problem.addConstraint(x1 + 3 * x2 <= 120, name="Labor_Hours") + problem.addConstraint(4 * x1 + 2 * x2 <= 200, name="Material") + problem.setObjective(50 * x1 + 30 * x2, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter("time_limit", 30) + problem.solve(settings) + + if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(f"Product A: {x1.getValue()}, Product B: {x2.getValue()}") + print(f"Total profit: {problem.ObjValue}") + else: + print(f"Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md new file mode 100644 index 0000000000..f18f4f549e --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md @@ -0,0 +1,88 @@ +# MPS File Solver + +Read and solve LP/MILP problems from standard MPS files using cuOpt. + +## Problem Description + +MPS (Mathematical Programming System) is a standard file format for representing linear and mixed-integer programming problems. This model demonstrates how to: + +1. Load an MPS file using `Problem.readMPS()` (static method) +2. Solve the problem using cuOpt's GPU-accelerated solver +3. Extract and display the solution + +This is useful when you have optimization problems in standard MPS format from other solvers, modeling tools, or benchmark libraries like MIPLIB. + +## MPS File Format + +MPS is a column-oriented format with sections: + +``` +NAME problem_name +ROWS + N OBJ (objective row) + L CON1 (≤ constraint) + G CON2 (≥ constraint) + E CON3 (= constraint) +COLUMNS + X1 OBJ 1.0 + X1 CON1 2.0 + X2 OBJ 2.0 + X2 CON1 3.0 +RHS + RHS CON1 10.0 +BOUNDS + LO BND X1 0.0 + UP BND X1 5.0 +ENDATA +``` + +## Usage + +```bash +# Solve the sample problem +python model.py + +# Solve a custom MPS file +python model.py --file path/to/problem.mps + +# With time limit +python model.py --file problem.mps --time-limit 120 +``` + +## Model Characteristics + +- **Type**: LP or MILP (detected from MPS file) +- **Input**: Standard MPS file format +- **Output**: Solution values, objective, status + +## Sample Problem + +The included `data/air05.mps` is a MIPLIB benchmark (airline crew scheduling): + +- **Variables**: 7,195 (binary) +- **Constraints**: 426 +- **Known optimal**: 26,374 +- **Typical solve time**: ~2 seconds + +## Key API Usage + +```python +from cuopt.linear_programming.problem import Problem +from cuopt.linear_programming.solver_settings import SolverSettings + +# Load MPS file (static method - returns Problem object) +problem = Problem.readMPS("path/to/problem.mps") + +# Configure and solve +settings = SolverSettings() +settings.set_parameter("time_limit", 60) +problem.solve(settings) + +# Check solution +if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(f"Objective: {problem.ObjValue}") +``` + +## Source + +Based on cuOpt's built-in MPS support via `Problem.readMPS()`. diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md new file mode 100644 index 0000000000..6f3dc9d823 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md @@ -0,0 +1,82 @@ +# MPS Solver Data + +This directory contains MPS files for testing. + +## Included Files + +### air05.mps (MIPLIB Benchmark) + +An airline crew scheduling problem from the MIPLIB benchmark library. + +| Property | Value | +|----------|-------| +| Type | Binary Integer Program | +| Variables | 7,195 (all binary) | +| Constraints | 426 | +| Non-zeros | 52,121 | +| Known Optimal | 26,374 | + +**Source**: https://miplib.zib.de/instance_details_air05.html + +**Problem**: Given flight legs and possible crew pairings, find the minimum-cost +set of pairings that covers all flight legs (set covering problem). + +## MPS File Format + +MPS (Mathematical Programming System) is a standard format for LP/MILP problems. + +### Sections + +| Section | Purpose | +|---------|---------| +| NAME | Problem name | +| ROWS | Constraint and objective definitions | +| COLUMNS | Variable coefficients in each row | +| RHS | Right-hand side values for constraints | +| BOUNDS | Variable bounds and types | +| ENDATA | End of file marker | + +### Row Types + +| Type | Meaning | +|------|---------| +| N | Objective function (no constraint) | +| L | Less than or equal (≤) | +| G | Greater than or equal (≥) | +| E | Equality (=) | + +### Bound Types + +| Type | Meaning | +|------|---------| +| LO | Lower bound | +| UP | Upper bound | +| FX | Fixed value (lb = ub) | +| FR | Free variable (-∞ to +∞) | +| BV | Binary variable (0 or 1) | +| UI | Upper bound, integer | +| LI | Lower bound, integer | + +## Adding Custom MPS Files + +```bash +python model.py --file path/to/your/problem.mps +``` + +## Standard Test Problem Sources + +- [MIPLIB](https://miplib.zib.de/) - Mixed Integer Programming Library +- [Netlib LP](https://www.netlib.org/lp/) - Classic LP test problems +- [NEOS](https://neos-server.org/neos/) - Network-Enabled Optimization System + +## Creating MPS Files + +cuOpt can export problems to MPS format: + +```python +from cuopt.linear_programming.problem import Problem + +problem = Problem("MyProblem") +# ... define variables, constraints, objective ... +problem.writeMPS("output.mps") +``` diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps new file mode 100644 index 0000000000..6baeb6e524 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps @@ -0,0 +1,19 @@ +NAME PRODUCTION_LP +ROWS + N PROFIT + L RES_A + L RES_B +COLUMNS + PROD_X PROFIT -40.0 + PROD_X RES_A 2.0 + PROD_X RES_B 4.0 + PROD_Y PROFIT -30.0 + PROD_Y RES_A 3.0 + PROD_Y RES_B 2.0 +RHS + RHS1 RES_A 120.0 + RHS1 RES_B 100.0 +BOUNDS + LO BND1 PROD_X 0.0 + LO BND1 PROD_Y 0.0 +ENDATA diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py new file mode 100644 index 0000000000..504ff1160d --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py @@ -0,0 +1,238 @@ +""" +MPS File Solver using cuOpt Python API + +Read and solve LP/MILP problems from standard MPS files using +cuOpt's built-in readMPS method. + +Default benchmark: air05.mps (airline crew scheduling from MIPLIB) +- Best known optimal: 26,374 +""" + +import os +import gzip +import urllib.request + +from cuopt.linear_programming.problem import Problem +from cuopt.linear_programming.solver_settings import SolverSettings + + +# MIPLIB benchmark URL +AIR05_URL = "https://miplib.zib.de/WebData/instances/air05.mps.gz" +AIR05_OPTIMAL = 26374 # Best known optimal solution + + +def download_air05(data_dir: str) -> str: + """Download air05.mps from MIPLIB if not present.""" + mps_file = os.path.join(data_dir, "air05.mps") + + if os.path.exists(mps_file): + return mps_file + + os.makedirs(data_dir, exist_ok=True) + gz_file = os.path.join(data_dir, "air05.mps.gz") + + print(f"Downloading air05.mps from MIPLIB...") + urllib.request.urlretrieve(AIR05_URL, gz_file) + + # Decompress + print("Decompressing...") + with gzip.open(gz_file, 'rb') as f_in: + with open(mps_file, 'wb') as f_out: + f_out.write(f_in.read()) + + # Clean up + os.remove(gz_file) + print(f"Downloaded: {mps_file}") + + return mps_file + + +def solve_mps( + filepath: str, + time_limit: float = 60.0, + mip_gap: float = 0.01, + verbose: bool = True +) -> tuple: + """ + Solve an LP/MILP problem from an MPS file. + + Parameters + ---------- + filepath : str + Path to the MPS file + time_limit : float + Solver time limit in seconds + mip_gap : float + MIP relative gap tolerance + verbose : bool + Print solver output + + Returns + ------- + tuple + (problem, solution_dict) or (problem, None) if no solution + """ + + # Read MPS file directly (static method returns Problem object) + problem = Problem.readMPS(filepath) + + print(f"Loaded MPS file: {filepath}") + print(f"Variables: {problem.NumVariables}") + print(f"Constraints: {problem.NumConstraints}") + print(f"Is MIP: {problem.IsMIP}") + + # Solver settings + settings = SolverSettings() + settings.set_parameter("time_limit", time_limit) + settings.set_parameter("log_to_console", verbose) + settings.set_parameter("mip_relative_gap", mip_gap) + + # Solve + print("\nSolving...") + problem.solve(settings) + + # Extract solution + status = problem.Status.name + print(f"\nStatus: {status}") + + if status in ["Optimal", "FeasibleFound", "PrimalFeasible"]: + solution = { + 'status': status, + 'objective': problem.ObjValue, + 'num_variables': problem.NumVariables, + 'num_constraints': problem.NumConstraints, + 'is_mip': problem.IsMIP, + 'mip_gap': mip_gap, + } + + # Get variable values (use getVariables() for MPS-loaded problems) + var_values = {} + try: + variables = problem.getVariables() + for var in variables: + val = var.getValue() + if abs(val) > 1e-6: # Only include non-zero values + var_values[var.Name] = val + except (AttributeError, Exception): + # For MPS problems, variable access may be limited + pass + + solution['variables'] = var_values + return problem, solution + else: + return problem, None + + +def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: + """ + Compare solutions at different MIP gap tolerances. + + Parameters + ---------- + filepath : str + Path to the MPS file + time_limit : float + Solver time limit per run + + Returns + ------- + dict + Results for each gap tolerance + """ + gaps = [0.01, 0.001] # 1% and 0.1% + results = {} + + for gap in gaps: + print(f"\n{'='*60}") + print(f"Solving with MIP gap = {gap*100}%") + print(f"{'='*60}") + + problem, solution = solve_mps( + filepath=filepath, + time_limit=time_limit, + mip_gap=gap, + verbose=True + ) + + if solution: + results[gap] = { + 'objective': solution['objective'], + 'status': solution['status'], + 'gap_to_optimal': (solution['objective'] - AIR05_OPTIMAL) / AIR05_OPTIMAL * 100 + } + else: + results[gap] = {'objective': None, 'status': 'No solution'} + + return results + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Solve LP/MILP from MPS file") + parser.add_argument("--file", type=str, default=None, help="Path to MPS file") + parser.add_argument("--time-limit", type=float, default=60.0, help="Solver time limit") + parser.add_argument("--mip-gap", type=float, default=0.01, help="MIP gap tolerance") + parser.add_argument("--compare", action="store_true", help="Compare 1% vs 0.1% gap") + args = parser.parse_args() + + print("=" * 60) + print("MPS File Solver using cuOpt") + print("=" * 60) + + # Determine MPS file to use + script_dir = os.path.dirname(os.path.abspath(__file__)) + data_dir = os.path.join(script_dir, "data") + + if args.file: + mps_file = args.file + else: + # Download air05.mps if not present + mps_file = download_air05(data_dir) + + if args.compare: + # Compare different gap tolerances + print(f"\nComparing MIP gap tolerances on: {mps_file}") + print(f"Best known optimal: {AIR05_OPTIMAL}") + + results = compare_gaps(mps_file, time_limit=args.time_limit) + + print() + print("=" * 60) + print("COMPARISON SUMMARY") + print("=" * 60) + print(f"Best known optimal: {AIR05_OPTIMAL}") + print() + print(f"{'Gap Tolerance':<15} {'Objective':<15} {'Gap to Optimal':<15}") + print("-" * 45) + + for gap, result in sorted(results.items()): + if result['objective']: + print(f"{gap*100:.1f}%{'':<12} {result['objective']:<15.0f} {result['gap_to_optimal']:.2f}%") + else: + print(f"{gap*100:.1f}%{'':<12} {'No solution':<15}") + else: + # Single solve + print(f"\nMPS File: {mps_file}") + print(f"Time Limit: {args.time_limit}s") + print(f"MIP Gap: {args.mip_gap * 100}%") + print() + + problem, solution = solve_mps( + filepath=mps_file, + time_limit=args.time_limit, + mip_gap=args.mip_gap, + verbose=True + ) + + if solution: + print() + print("=" * 60) + print("SOLUTION") + print("=" * 60) + print(f"Status: {solution['status']}") + print(f"Objective Value: {solution['objective']:.0f}") + print(f"Best Known Optimal: {AIR05_OPTIMAL}") + print(f"Gap to Optimal: {(solution['objective'] - AIR05_OPTIMAL) / AIR05_OPTIMAL * 100:.2f}%") + else: + print("\nNo feasible solution found.") diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md new file mode 100644 index 0000000000..4100dea6b2 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md @@ -0,0 +1,90 @@ +# MPS Solver Results + +## Problem: air05.mps (MIPLIB benchmark) + +**Description:** Airline crew scheduling - set partitioning problem + +### Problem Characteristics +- **Variables:** 7195 (all binary) +- **Constraints:** 426 +- **Nonzeros:** 52121 +- **Best Known Optimal:** 26374 + +--- + +## Gap Tolerance Comparison + +Comparing different MIP relative gap tolerances to show trade-off between solution quality and solve time. + +### Run Configuration +- **Time Limit:** 60 seconds +- **cuOpt Version:** 26.2.0 +- **Device:** Quadro RTX 8000 (47.24 GiB VRAM) +- **CPU:** AMD Ryzen Threadripper PRO 3975WX (32 cores) + +### Results Summary + +| Gap Tolerance | Objective | Gap to Optimal | Solve Time | Nodes Explored | +|--------------|-----------|----------------|------------|----------------| +| 0.1% | **26374** | 0.00% | 8.42s | 386 | +| 1.0% | 26491 | 0.44% | 3.23s | 328 | + +### Key Observations + +1. **Tighter gap finds optimal**: The 0.1% gap tolerance found the exact best-known optimal solution (26374) +2. **Trade-off**: The looser 1.0% gap converged faster (3.2s vs 8.4s) but with 0.44% suboptimality +3. **Both are fast**: cuOpt solved this 7195-variable MILP in under 10 seconds + +--- + +## Detailed Solver Output (0.1% gap) + +``` +Solving a problem with 426 constraints, 7195 variables (7195 integers), and 52121 nonzeros + +Presolve removed: 90 constraints, 1116 variables, 16171 nonzeros +Presolved problem: 336 constraints, 6079 variables, 35950 nonzeros + +Root relaxation objective +2.58776093e+04 + +Strong branching using 7 threads and 222 fractional variables +Explored 386 nodes in 7.73s. + +Optimal solution found within relative MIP gap tolerance (1.0e-03) +Solution objective: 26374.000000 +relative_mip_gap 0.000992 +total_solve_time 8.421934 +``` + +--- + +## Detailed Solver Output (1.0% gap) + +``` +Solving a problem with 426 constraints, 7195 variables (7195 integers), and 52121 nonzeros + +Presolve removed: 90 constraints, 1116 variables, 16171 nonzeros +Presolved problem: 336 constraints, 6079 variables, 35950 nonzeros + +Root relaxation objective +2.58776093e+04 + +Strong branching using 63 threads and 222 fractional variables +Explored 328 nodes in 1.09s. + +Optimal solution found within relative MIP gap tolerance (1.0e-02) +Solution objective: 26491.000000 +relative_mip_gap 0.009669 +total_solve_time 3.233650 +``` + +--- + +## Usage + +```bash +# Default: download air05.mps and solve with comparison +python model.py --compare --time-limit 60 + +# Solve custom MPS file +python model.py --file path/to/problem.mps --time-limit 300 --mip-gap 0.001 +``` diff --git a/.github/skills/cuopt-lp-milp-api-python/resources/examples.md b/.github/skills/cuopt-lp-milp-api-python/resources/examples.md deleted file mode 100644 index eaaee59d48..0000000000 --- a/.github/skills/cuopt-lp-milp-api-python/resources/examples.md +++ /dev/null @@ -1,257 +0,0 @@ -# LP/MILP: Python API Examples - -## Linear Programming (LP) - -```python -""" -Production Planning LP: - maximize 40*chairs + 30*tables (profit) - subject to 2*chairs + 3*tables <= 240 (wood constraint) - 4*chairs + 2*tables <= 200 (labor constraint) - chairs, tables >= 0 -""" -from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE -from cuopt.linear_programming.solver_settings import SolverSettings - -# Create problem -problem = Problem("ProductionPlanning") - -# Decision variables (continuous, non-negative) -chairs = problem.addVariable(lb=0, vtype=CONTINUOUS, name="chairs") -tables = problem.addVariable(lb=0, vtype=CONTINUOUS, name="tables") - -# Constraints -problem.addConstraint(2 * chairs + 3 * tables <= 240, name="wood") -problem.addConstraint(4 * chairs + 2 * tables <= 200, name="labor") - -# Objective: maximize profit -problem.setObjective(40 * chairs + 30 * tables, sense=MAXIMIZE) - -# Solver settings -settings = SolverSettings() -settings.set_parameter("time_limit", 60) -settings.set_parameter("log_to_console", 1) - -# Solve -problem.solve(settings) - -# Check status and extract results -status = problem.Status.name -print(f"Status: {status}") - -if status in ["Optimal", "PrimalFeasible"]: - print(f"Optimal profit: ${problem.ObjValue:.2f}") - print(f"Chairs to produce: {chairs.getValue():.1f}") - print(f"Tables to produce: {tables.getValue():.1f}") - - # Get dual values (shadow prices) - wood_constraint = problem.getConstraint("wood") - labor_constraint = problem.getConstraint("labor") - print(f"\nShadow price (wood): ${wood_constraint.DualValue:.2f} per unit") - print(f"Shadow price (labor): ${labor_constraint.DualValue:.2f} per unit") -else: - print(f"No optimal solution found. Status: {status}") -``` - -## Mixed-Integer Linear Programming (MILP) - -```python -""" -Facility Location MILP: -- Decide which warehouses to open (binary) -- Assign customers to open warehouses -- Minimize fixed costs + transportation costs -""" -from cuopt.linear_programming.problem import ( - Problem, CONTINUOUS, INTEGER, MINIMIZE -) -from cuopt.linear_programming.solver_settings import SolverSettings - -# Problem data -warehouses = ["W1", "W2", "W3"] -customers = ["C1", "C2", "C3", "C4"] - -fixed_costs = {"W1": 100, "W2": 150, "W3": 120} -capacities = {"W1": 50, "W2": 70, "W3": 60} -demands = {"C1": 20, "C2": 25, "C3": 15, "C4": 30} - -transport_cost = { - ("W1", "C1"): 5, ("W1", "C2"): 8, ("W1", "C3"): 6, ("W1", "C4"): 10, - ("W2", "C1"): 7, ("W2", "C2"): 4, ("W2", "C3"): 9, ("W2", "C4"): 5, - ("W3", "C1"): 6, ("W3", "C2"): 7, ("W3", "C3"): 4, ("W3", "C4"): 8, -} - -# Create problem -problem = Problem("FacilityLocation") - -# Binary variables: y[w] = 1 if warehouse w is open -y = {w: problem.addVariable(lb=0, ub=1, vtype=INTEGER, name=f"open_{w}") - for w in warehouses} - -# Continuous variables: x[w,c] = units shipped from w to c -x = {(w, c): problem.addVariable(lb=0, vtype=CONTINUOUS, name=f"ship_{w}_{c}") - for w in warehouses for c in customers} - -# Objective: minimize fixed + transportation costs -problem.setObjective( - sum(fixed_costs[w] * y[w] for w in warehouses) + - sum(transport_cost[w, c] * x[w, c] for w in warehouses for c in customers), - sense=MINIMIZE -) - -# Constraints: meet customer demand -for c in customers: - problem.addConstraint( - sum(x[w, c] for w in warehouses) == demands[c], - name=f"demand_{c}" - ) - -# Constraints: respect warehouse capacity (only if open) -for w in warehouses: - problem.addConstraint( - sum(x[w, c] for c in customers) <= capacities[w] * y[w], - name=f"capacity_{w}" - ) - -# Solver settings -settings = SolverSettings() -settings.set_parameter("time_limit", 120) -settings.set_parameter("mip_relative_gap", 0.01) - -# Solve -problem.solve(settings) - -# Results -status = problem.Status.name -print(f"Status: {status}") - -if status in ["Optimal", "FeasibleFound"]: - print(f"Total cost: ${problem.ObjValue:.2f}") - print("\nOpen warehouses:") - for w in warehouses: - if y[w].getValue() > 0.5: - print(f" {w} (fixed cost: ${fixed_costs[w]})") - - print("\nShipments:") - for w in warehouses: - for c in customers: - shipped = x[w, c].getValue() - if shipped > 0.01: - print(f" {w} -> {c}: {shipped:.1f} units") -``` - -## Knapsack Problem (MILP) - -```python -""" -0/1 Knapsack: select items to maximize value within weight limit -""" -from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE -from cuopt.linear_programming.solver_settings import SolverSettings - -items = ["laptop", "camera", "phone", "tablet", "headphones"] -values = [1000, 500, 300, 600, 150] -weights = [3, 1, 0.5, 1.5, 0.3] -max_weight = 5 - -problem = Problem("Knapsack") - -# Binary variables: x[i] = 1 if item i is selected -x = [problem.addVariable(lb=0, ub=1, vtype=INTEGER, name=items[i]) - for i in range(len(items))] - -# Objective: maximize total value -problem.setObjective(sum(values[i] * x[i] for i in range(len(items))), sense=MAXIMIZE) - -# Constraint: weight limit -problem.addConstraint(sum(weights[i] * x[i] for i in range(len(items))) <= max_weight) - -problem.solve(SolverSettings()) - -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(f"Total value: ${problem.ObjValue:.0f}") - print("Selected items:") - for i, item in enumerate(items): - if x[i].getValue() > 0.5: - print(f" {item}: value=${values[i]}, weight={weights[i]}") -``` - -## Transportation Problem (LP) - -```python -""" -Minimize shipping cost from suppliers to customers -""" -from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE - -suppliers = ["S1", "S2"] -customers = ["C1", "C2", "C3"] -supply = {"S1": 100, "S2": 150} -demand = {"C1": 80, "C2": 70, "C3": 100} -cost = { - ("S1", "C1"): 4, ("S1", "C2"): 6, ("S1", "C3"): 8, - ("S2", "C1"): 5, ("S2", "C2"): 3, ("S2", "C3"): 7, -} - -problem = Problem("Transportation") - -x = {(s, c): problem.addVariable(lb=0, vtype=CONTINUOUS, name=f"x_{s}_{c}") - for s in suppliers for c in customers} - -# Minimize total shipping cost -problem.setObjective(sum(cost[s,c] * x[s,c] for s in suppliers for c in customers), - sense=MINIMIZE) - -# Supply constraints -for s in suppliers: - problem.addConstraint(sum(x[s,c] for c in customers) <= supply[s]) - -# Demand constraints -for c in customers: - problem.addConstraint(sum(x[s,c] for s in suppliers) >= demand[c]) - -problem.solve() - -if problem.Status.name in ("Optimal", "PrimalFeasible"): - print(f"Total cost: ${problem.ObjValue:.2f}") - for s in suppliers: - for c in customers: - val = x[s,c].getValue() - if val > 0.01: - print(f" {s} -> {c}: {val:.0f} units") -``` - -## Status Checking (Critical) - -```python -# ✅ CORRECT - use PascalCase -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(problem.ObjValue) - -# ❌ WRONG - will silently fail! -if problem.Status.name == "OPTIMAL": # Never matches! - print(problem.ObjValue) - -# LP status values: Optimal, PrimalFeasible, PrimalInfeasible, -# DualInfeasible, TimeLimit, NumericalError -# MILP status values: Optimal, FeasibleFound, Infeasible, -# Unbounded, TimeLimit, NoTermination -``` - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | Description | -|---------|------|-------------| -| Simple LP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_lp_example.py` | Basic LP setup | -| Simple MILP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_milp_example.py` | Integer variables | -| Production Planning | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/production_planning_example.py` | Real-world LP | -| Expressions | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/expressions_constraints_example.py` | Advanced constraint syntax | -| Incumbent Solutions | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/incumbent_solutions_example.py` | Tracking MIP progress | -| Warmstart | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/pdlp_warmstart_example.py` | Warm starting LP | -| Solution Handling | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/solution_example.py` | Working with results | - -These examples are tested by CI (`ci/test_doc_examples.sh`) and represent canonical usage. diff --git a/.github/skills/cuopt-lp-milp-api-python/resources/server_examples.md b/.github/skills/cuopt-lp-milp-api-python/resources/server_examples.md deleted file mode 100644 index 521d8a6ead..0000000000 --- a/.github/skills/cuopt-lp-milp-api-python/resources/server_examples.md +++ /dev/null @@ -1,208 +0,0 @@ -# LP/MILP: REST Server Examples - -## LP Request (curl) - -```bash -# Production Planning LP via REST -# maximize 40*chairs + 30*tables -# s.t. 2*chairs + 3*tables <= 240 -# 4*chairs + 2*tables <= 200 - -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": true, - "solver_config": { - "tolerances": {"optimality": 0.0001}, - "time_limit": 60 - } - }' | jq -r '.reqId') - -echo "Request ID: $REQID" - -# Get solution -sleep 2 -curl -s "http://localhost:8000/cuopt/solution/$REQID" \ - -H "CLIENT-VERSION: custom" | jq . -``` - -## MILP Request (curl) - -```bash -# Add integer variable types -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0] - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "variable_types": ["integer", "continuous"], - "maximize": true, - "solver_config": { - "time_limit": 120, - "tolerances": { - "mip_relative_gap": 0.01 - } - } - }' | jq -r '.reqId') - -echo "Request ID: $REQID" - -# Poll for solution (MILP may take longer than LP) -while true; do - RESULT=$(curl -s "http://localhost:8000/cuopt/solution/$REQID" \ - -H "CLIENT-VERSION: custom") - STATUS=$(echo "$RESULT" | jq -r '.response.status // empty') - if [ -n "$STATUS" ]; then - echo "$RESULT" | jq . - break - fi - sleep 2 -done -``` - -## LP Request (Python) - -```python -import requests -import time - -SERVER = "http://localhost:8000" -HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} - -payload = { - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": True, - "solver_config": { - "time_limit": 60 - } -} - -# Submit -response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) -req_id = response.json()["reqId"] -print(f"Submitted: {req_id}") - -# Poll for solution -for _ in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) - result = response.json() - - if "response" in result: - print(f"Status: {result['response'].get('status')}") - print(f"Objective: {result['response'].get('objective_value')}") - print(f"Solution: {result['response'].get('primal_solution')}") - break - time.sleep(1) -``` - -## CSR Matrix Format - -The constraint matrix uses Compressed Sparse Row (CSR) format: - -``` -Matrix: [2, 3] (row 0: 2*x0 + 3*x1) - [4, 2] (row 1: 4*x0 + 2*x1) - -CSR format: - offsets: [0, 2, 4] # Row pointers - indices: [0, 1, 0, 1] # Column indices - values: [2.0, 3.0, 4.0, 2.0] # Non-zero values -``` - -## Special Values - -```json -{ - "constraint_bounds": { - "lower_bounds": ["ninf", "ninf"], - "upper_bounds": [100.0, "inf"] - } -} -``` - -- `"ninf"` — negative infinity (-∞) -- `"inf"` — positive infinity (+∞) - -## Variable Types - -```json -{ - "variable_types": ["continuous", "integer", "binary"] -} -``` - -- `"continuous"` - real-valued -- `"integer"` - integer-valued -- `"binary"` - 0 or 1 only - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | Description | -|---------|------|-------------| -| Basic LP (Python) | `docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.py` | LP via REST | -| Basic LP (curl) | `docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.sh` | LP shell script | -| MPS Input | `docs/cuopt/source/cuopt-server/examples/lp/examples/mps_file_example.py` | MPS file format | -| MPS DataModel | `docs/cuopt/source/cuopt-server/examples/lp/examples/mps_datamodel_example.py` | MPS in payload | -| Warmstart | `docs/cuopt/source/cuopt-server/examples/lp/examples/warmstart_example.py` | Warm starting | -| Basic MILP (Python) | `docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.py` | MILP via REST | -| Basic MILP (curl) | `docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.sh` | MILP shell script | -| Incumbent Callback | `docs/cuopt/source/cuopt-server/examples/milp/examples/incumbent_callback_example.py` | MIP progress tracking | -| Abort Job | `docs/cuopt/source/cuopt-server/examples/milp/examples/abort_job_example.py` | Canceling requests | -| Batch Mode | `docs/cuopt/source/cuopt-server/examples/lp/examples/batch_mode_example.sh` | Multiple problems | - -These examples are tested by CI (`ci/test_doc_examples.sh`) and represent canonical usage. From 208f8826a4502b40e37e67723634945dc2bf27ee Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 27 Feb 2026 14:59:30 -0600 Subject: [PATCH 05/35] update --- .github/AGENTS.md | 6 +- .github/skills/cuopt-lp-milp-api-c/SKILL.md | 6 +- .github/skills/cuopt-lp-milp-api-cli/SKILL.md | 4 +- .../skills/cuopt-lp-milp-api-python/SKILL.md | 2 +- .github/skills/cuopt-lp-milp-common/SKILL.md | 29 ---- .../skills/cuopt-lp-milp-formulation/SKILL.md | 127 ++++++++++++++++++ .github/skills/cuopt-qp-api-c/SKILL.md | 6 +- .github/skills/cuopt-qp-api-cli/SKILL.md | 6 +- .github/skills/cuopt-qp-api-python/SKILL.md | 6 +- .../SKILL.md | 6 +- .../skills/cuopt-routing-api-python/SKILL.md | 6 +- .../SKILL.md | 6 +- 12 files changed, 154 insertions(+), 56 deletions(-) delete mode 100644 .github/skills/cuopt-lp-milp-common/SKILL.md create mode 100644 .github/skills/cuopt-lp-milp-formulation/SKILL.md rename .github/skills/{cuopt-qp-common => cuopt-qp-formulation}/SKILL.md (87%) rename .github/skills/{cuopt-routing-common => cuopt-routing-formulation}/SKILL.md (86%) diff --git a/.github/AGENTS.md b/.github/AGENTS.md index e6ad330723..4a577c10e9 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -16,9 +16,9 @@ Choose which skills to load from the index; - `skills/cuopt-developer/` — Contributing (own rules) ### Common (concepts only; no API code) -- `skills/cuopt-lp-milp-common/` — LP/MILP: when to use, escalate -- `skills/cuopt-routing-common/` — Routing: VRP, TSP, PDP -- `skills/cuopt-qp-common/` — QP: minimize-only, escalate +- `skills/cuopt-lp-milp-formulation/` — LP/MILP: concepts + problem parsing (parameters, constraints, decisions, objective) +- `skills/cuopt-routing-formulation/` — Routing: VRP, TSP, PDP (problem types, data) +- `skills/cuopt-qp-formulation/` — QP: minimize-only, escalate (beta) - `skills/cuopt-server-common/` — Server: capabilities, workflow ### API (implementation; one interface per skill) diff --git a/.github/skills/cuopt-lp-milp-api-c/SKILL.md b/.github/skills/cuopt-lp-milp-api-c/SKILL.md index 453c3348f7..bc54cfe91a 100644 --- a/.github/skills/cuopt-lp-milp-api-c/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-c/SKILL.md @@ -1,11 +1,11 @@ --- name: cuopt-lp-milp-api-c -description: LP and MILP with cuOpt — C API only. Use with cuopt-lp-milp-common for concepts. Use when the user is embedding LP/MILP in C/C++. +description: LP and MILP with cuOpt — C API only. Use with cuopt-lp-milp-formulation for concepts. Use when the user is embedding LP/MILP in C/C++. --- # cuOpt LP/MILP — C API -**Concepts:** Read `cuopt-lp-milp-common/SKILL.md` for problem type, when to use, when to escalate. +**Concepts:** Read `cuopt-lp-milp-formulation/SKILL.md` for problem type and formulation. This skill is **C only**. For Python, use `cuopt-lp-milp-api-python`. @@ -46,4 +46,4 @@ For **CLI** (MPS files), use `cuopt-lp-milp-api-cli`. ## Escalate -See `cuopt-lp-milp-common` for when to use cuopt-qp-common or cuopt-developer. +See `cuopt-lp-milp-formulation` for when to use cuopt-qp-formulation or cuopt-developer. diff --git a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md index 0c8d0fb167..d1064ef5df 100644 --- a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md @@ -1,11 +1,11 @@ --- name: cuopt-lp-milp-api-cli -description: LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use with cuopt-lp-milp-common for concepts. Use when the user is solving from MPS via command line. +description: LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use with cuopt-lp-milp-formulation for concepts. Use when the user is solving from MPS via command line. --- # cuOpt LP/MILP — CLI -**Concepts:** Read `cuopt-lp-milp-common/SKILL.md` for problem type and formulation. +**Concepts:** Read `cuopt-lp-milp-formulation/SKILL.md` for problem type and formulation. This skill is **CLI only**. For Python or C, use `cuopt-lp-milp-api-python` or `cuopt-lp-milp-api-c`. diff --git a/.github/skills/cuopt-lp-milp-api-python/SKILL.md b/.github/skills/cuopt-lp-milp-api-python/SKILL.md index 717be801da..fb771b5e67 100644 --- a/.github/skills/cuopt-lp-milp-api-python/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-python/SKILL.md @@ -9,7 +9,7 @@ Model and solve linear and mixed-integer linear programs using NVIDIA cuOpt's GP ## Before You Start -Use the summary from **problem-statement-parsing** (parameters, constraints, decisions, objective) if available; otherwise ask for decision variables, objective, and constraints. Then confirm **variable types** (see below) and **interface** (Python API recommended). +Use the summary from **cuopt-lp-milp-formulation** (parameters, constraints, decisions, objective) if available; otherwise ask for decision variables, objective, and constraints. Then confirm **variable types** (see below) and **interface** (Python API recommended). ## Choosing LP vs MILP diff --git a/.github/skills/cuopt-lp-milp-common/SKILL.md b/.github/skills/cuopt-lp-milp-common/SKILL.md deleted file mode 100644 index 45acfef614..0000000000 --- a/.github/skills/cuopt-lp-milp-common/SKILL.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: cuopt-lp-milp-common -description: LP and MILP with cuOpt — problem type and formulation only. Domain concepts; no API code and no interface or escalation guidance. ---- - -# cuOpt LP/MILP (common) - -Domain concepts for linear and mixed-integer linear programming. No API or interface details here. - -## What is LP / MILP - -- **LP**: Linear objective, linear constraints, continuous variables. -- **MILP**: Same plus some integer or binary variables (e.g. scheduling, facility location, selection). - -## Required questions (problem formulation) - -Ask these if not already clear: - -1. **Decision variables** — What are they? Bounds? -2. **Objective** — Minimize or maximize? Linear expression in the variables? -3. **Constraints** — Linear inequalities/equalities? Names and meaning? -4. **Variable types** — All continuous (LP) or some integer/binary (MILP)? - -## Typical modeling elements - -- **Continuous variables** — production amounts, flow, etc. -- **Binary variables** — open/close, yes/no (e.g. facility open, item selected). -- **Linking constraints** — e.g. production only if facility open (Big-M or indicator). -- **Resource constraints** — linear cap on usage (materials, time, capacity). diff --git a/.github/skills/cuopt-lp-milp-formulation/SKILL.md b/.github/skills/cuopt-lp-milp-formulation/SKILL.md new file mode 100644 index 0000000000..a482a01f06 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-formulation/SKILL.md @@ -0,0 +1,127 @@ +--- +name: cuopt-lp-milp-formulation +description: LP/MILP concepts and going from problem text to formulation. What LP/MILP are, required formulation questions, typical modeling elements, and how to parse problem statements (parameters, constraints, decisions, objective). Use before coding with cuopt-lp-milp-api-python/c/cli. +--- + +# cuOpt LP/MILP Formulation + +Concepts and workflow for going from a problem description to a clear formulation. No API code here — use the API skills to implement. + +## What is LP / MILP + +- **LP**: Linear objective, linear constraints, continuous variables. +- **MILP**: Same plus some integer or binary variables (e.g. scheduling, facility location, selection). + +## Required questions (problem formulation) + +Ask these if not already clear: + +1. **Decision variables** — What are they? Bounds? +2. **Objective** — Minimize or maximize? Linear expression in the variables? +3. **Constraints** — Linear inequalities/equalities? Names and meaning? +4. **Variable types** — All continuous (LP) or some integer/binary (MILP)? + +## Typical modeling elements + +- **Continuous variables** — production amounts, flow, etc. +- **Binary variables** — open/close, yes/no (e.g. facility open, item selected). +- **Linking constraints** — e.g. production only if facility open (Big-M or indicator). +- **Resource constraints** — linear cap on usage (materials, time, capacity). + +--- + +## Problem statement parsing + +When the user gives **problem text**, classify every sentence and then summarize before formulating. + +**Classify every sentence** as **parameter/given**, **constraint**, **decision**, or **objective**. Watch for **implicit constraints** (e.g. committed vs optional phrasing) and **implicit objectives** (e.g. "determine the plan" + costs → minimize total cost). + +**Ambiguity:** If anything is still ambiguous, ask the user or solve all plausible interpretations and report all outcomes; do not assume a single interpretation. + +### 🔒 MANDATORY: When in Doubt — Ask + +- If there is **any doubt** about whether a constraint or value should be included, **ask the user** and state the possible interpretations. + +### 🔒 MANDATORY: Complete-Path Runs — Try All Variants + +- When the user asks to **run the complete path** (e.g. end-to-end, full pipeline), run all plausible variants and **report all outcomes** so the user can choose; do not assume a single interpretation. + +### Three labels + +| Label | Meaning | Examples (sentence type) | +|-------|--------|---------------------------| +| **Parameter / given** | Fixed data, inputs, facts. Not chosen by the model. | "Demand is 100 units." "There are 3 factories." "Costs are $5 per unit." | +| **Constraint** | Something that must hold. May be explicit or **implicit** from phrasing. | "Capacity is 200." "All demand must be met." "At least 2 shifts must be staffed." | +| **Decision** | Something we choose or optimize. | "How much to produce." "Which facilities to open." "How many workers to hire." | +| **Objective** | What to minimize or maximize. May be **explicit** ("minimize cost") or **implicit** ("determine the plan" with costs given). | "Minimize total cost." "Determine the production plan" (with costs) → minimize total cost. | + +### Implicit constraints: committed vs optional phrasing + +**Committed/fixed phrasing** → treat as **parameter** or **implicit constraint** (everything mentioned is given or must happen). Not a decision. + +| Phrasing | Interpretation | Why | +|----------|-----------------|-----| +| "Plans to produce X products" | **Constraint**: all X must be produced. | Commitment; production level is fixed. | +| "Operates 3 factories" | **Parameter**: all 3 are open. Not a location-selection problem. | Current state is fixed. | +| "Employs N workers" | **Parameter**: all N are employed. Not a hiring decision. | Workforce size is given. | +| "Has a capacity of C" | **Parameter** (C) + **constraint**: usage ≤ C. | Capacity is fixed. | +| "Must meet all demand" | **Constraint**: demand satisfaction. | Explicit requirement. | + +**Optional/decision phrasing** → treat as **decision**. + +| Phrasing | Interpretation | Why | +|----------|-----------------|-----| +| "May produce up to …" | **Decision**: how much to produce. | Optional level. | +| "Can choose to open" (factories, sites) | **Decision**: which to open. | Selection is decided. | +| "Considers hiring" | **Decision**: how many to hire. | Hiring is under consideration. | +| "Decides how much to order" | **Decision**: order quantities. | Explicit decision. | +| "Wants to minimize/maximize …" | **Objective** (drives decisions). | Goal; decisions are the levers. | + +### Implicit objectives — do not miss + +**If the problem asks to "determine the plan" (or similar) but does not state "minimize" or "maximize" explicitly, the objective is often implicit.** You **MUST** identify it and state it before formulating; do not build a model with no objective. + +| Phrasing / context | Likely implicit objective | Why | +|-------------------|---------------------------|-----| +| "Determine the production plan" + costs given (per unit, per hour, etc.) | **Minimize total cost** (production + inspection/sales + overtime, etc.) | Plan is chosen; costs are specified → natural goal is to minimize total cost. | +| "Determine the plan" + costs and revenues given | **Maximize profit** (revenue − cost) | Both sides of the ledger → optimize profit. | +| "Try to determine the monthly production plan" + workshop hour costs, inspection/sales costs | **Minimize total cost** | All cost components are given; no revenue to maximize → minimize total cost. | + +**Rule:** When the problem gives cost (or cost and revenue) data and asks to "determine", "find", or "establish" the plan, **always state the objective explicitly** (e.g. "I'm treating the objective as minimize total cost, since only costs are given."). If both cost and revenue are present, state whether you use "minimize cost" or "maximize profit". Ask the user if unclear. + +### Parsing workflow + +1. **Split** the problem text into sentences or logical clauses. +2. **Label** each: parameter/given | constraint | decision | **objective** (if stated). +3. **Identify the objective (explicit or implicit):** If the problem says "minimize/maximize X", that's the objective. If it only says "determine the plan" (or "find", "establish") but gives costs (and possibly revenues), the objective is **implicit** — state it (e.g. minimize total cost, or maximize profit) and confirm with the user if ambiguous. +4. **Flag implicit constraints**: For each sentence, ask — "Does this state a fixed fact or a requirement (→ parameter/constraint), or something we choose (→ decision)?" +5. **Resolve ambiguity** by checking verbs and modals: + - "is", "has", "operates", "employs", "plans to" (fixed/committed) → parameter or implicit constraint. + - "may", "can choose", "considers", "decides", "wants to" (optional) → decision or objective. +6. **🔒 MANDATORY — If anything is still ambiguous** (e.g. a value or constraint could be read two ways): ask the user which interpretation is correct, or solve all plausible interpretations and report all outcomes. Do not assume a single interpretation. +7. **Summarize** for the user: list parameters, constraints (explicit + flagged implicit), decisions, and **objective (explicit or inferred)** before writing the math formulation. + +### Parsing checklist + +- [ ] Every sentence has a label (parameter | constraint | decision | objective if stated). +- [ ] **Objective is identified:** Explicit ("minimize/maximize X") or implicit ("determine the plan" + costs → minimize total cost; + revenues → maximize profit). Never formulate without stating the objective. +- [ ] Committed phrasing ("plans to", "operates", "employs") → not decisions. +- [ ] Optional phrasing ("may", "can choose", "considers") → decisions. +- [ ] Implicit constraints from committed phrasing are written out (e.g. "all X must be produced"). +- [ ] **🔒 MANDATORY — Ambiguity:** Any phrase that could be read two ways → I asked the user or I will solve all interpretations and report all outcomes (no silent single interpretation). +- [ ] Summary is produced before formulating (parameters, constraints, decisions, **objective**). + +### Example + +**Text:** "The company operates 3 factories and plans to produce 500 units. It may use overtime at extra cost. Minimize total cost." + +| Sentence / phrase | Label | Note | +|-------------------|-------|------| +| "Operates 3 factories" | Parameter | All 3 open; not facility selection. | +| "Plans to produce 500 units" | Constraint (implicit) | All 500 must be produced. | +| "May use overtime at extra cost" | Decision | How much overtime is a decision. | +| "Minimize total cost" | Objective | Drives decisions. | + +Result: Parameters = 3 factories, 500 units target. Constraints = produce exactly 500 (implicit from "plans to produce"). Decisions = production allocation across factories, overtime amounts. Objective = minimize cost. + +**Implicit-objective example:** "Try to determine the monthly production plan" with workshop hour costs (80/20 yuan), inspection/sales costs (30/50 per unit), and no stated "minimize/maximize" → **Objective is implicit: minimize total cost** (workshop + inspection/sales + any overtime). Always state it: "The objective is to minimize total cost." diff --git a/.github/skills/cuopt-qp-api-c/SKILL.md b/.github/skills/cuopt-qp-api-c/SKILL.md index be2ba8271d..3d82e82126 100644 --- a/.github/skills/cuopt-qp-api-c/SKILL.md +++ b/.github/skills/cuopt-qp-api-c/SKILL.md @@ -1,11 +1,11 @@ --- name: cuopt-qp-api-c -description: Quadratic Programming (QP) with cuOpt — C API. Use with cuopt-qp-common for concepts. Use when the user is embedding QP in C/C++. +description: Quadratic Programming (QP) with cuOpt — C API. Use with cuopt-qp-formulation for concepts. Use when the user is embedding QP in C/C++. --- # cuOpt QP — C API -**Concepts:** Read `cuopt-qp-common/SKILL.md` for when QP applies, minimize-only, when to escalate. +**Concepts:** Read `cuopt-qp-formulation/SKILL.md` for when QP applies, minimize-only, when to escalate. This skill is **C only**. For Python (beta), use `cuopt-qp-api-python`. @@ -13,4 +13,4 @@ QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic object ## Escalate -See `cuopt-qp-common` for when to use cuopt-lp-milp-common or cuopt-developer. +See `cuopt-qp-formulation` for when to use cuopt-lp-milp-formulation or cuopt-developer. diff --git a/.github/skills/cuopt-qp-api-cli/SKILL.md b/.github/skills/cuopt-qp-api-cli/SKILL.md index fdb1257515..aef0b82527 100644 --- a/.github/skills/cuopt-qp-api-cli/SKILL.md +++ b/.github/skills/cuopt-qp-api-cli/SKILL.md @@ -1,11 +1,11 @@ --- name: cuopt-qp-api-cli -description: QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use with cuopt-qp-common for concepts. Use when the user is solving QP from the command line. +description: QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use with cuopt-qp-formulation for concepts. Use when the user is solving QP from the command line. --- # cuOpt QP — CLI -**Concepts:** Read `cuopt-qp-common/SKILL.md` for when QP applies and the **minimize-only** rule. +**Concepts:** Read `cuopt-qp-formulation/SKILL.md` for when QP applies and the **minimize-only** rule. This skill is **CLI only** for QP. For Python or C, use `cuopt-qp-api-python` or `cuopt-qp-api-c`. @@ -33,4 +33,4 @@ CLI is included with the Python package (`cuopt`). Install via pip or conda (see ## Escalate -See `cuopt-qp-common` for when to use cuopt-lp-milp-common or cuopt-developer. +See `cuopt-qp-formulation` for when to use cuopt-lp-milp-formulation or cuopt-developer. diff --git a/.github/skills/cuopt-qp-api-python/SKILL.md b/.github/skills/cuopt-qp-api-python/SKILL.md index 1b69229a9e..c1fad47bf8 100644 --- a/.github/skills/cuopt-qp-api-python/SKILL.md +++ b/.github/skills/cuopt-qp-api-python/SKILL.md @@ -1,11 +1,11 @@ --- name: cuopt-qp-api-python -description: Quadratic Programming (QP) with cuOpt — Python API only (beta). Use with cuopt-qp-common for concepts. Use when the user is building or solving QP in Python. +description: Quadratic Programming (QP) with cuOpt — Python API only (beta). Use with cuopt-qp-formulation for concepts. Use when the user is building or solving QP in Python. --- # cuOpt QP — Python API (beta) -**Concepts:** Read `cuopt-qp-common/SKILL.md` for when QP applies, minimize-only, when to escalate. +**Concepts:** Read `cuopt-qp-formulation/SKILL.md` for when QP applies, minimize-only, when to escalate. This skill is **Python only**. For C, use `cuopt-qp-api-c`. **QP is beta.** @@ -55,4 +55,4 @@ if problem.Status.name in ["Optimal", "PrimalFeasible"]: ## Escalate -See `cuopt-qp-common` for when to use cuopt-lp-milp-common or cuopt-developer. +See `cuopt-qp-formulation` for when to use cuopt-lp-milp-formulation or cuopt-developer. diff --git a/.github/skills/cuopt-qp-common/SKILL.md b/.github/skills/cuopt-qp-formulation/SKILL.md similarity index 87% rename from .github/skills/cuopt-qp-common/SKILL.md rename to .github/skills/cuopt-qp-formulation/SKILL.md index 6c3b2018d5..84e88714be 100644 --- a/.github/skills/cuopt-qp-common/SKILL.md +++ b/.github/skills/cuopt-qp-formulation/SKILL.md @@ -1,9 +1,9 @@ --- -name: cuopt-qp-common -description: Quadratic Programming (QP) with cuOpt — problem form and constraints only. Domain concepts; no API or interface guidance. QP is beta. +name: cuopt-qp-formulation +description: Quadratic Programming (QP) with cuOpt — problem form and constraints. Domain concepts; no API or interface. QP is beta. Use before coding with cuopt-qp-api-python/c/cli. --- -# cuOpt QP (common) +# cuOpt QP Formulation Domain concepts for quadratic programming. No API or interface details here. **QP support in cuOpt is currently in beta.** diff --git a/.github/skills/cuopt-routing-api-python/SKILL.md b/.github/skills/cuopt-routing-api-python/SKILL.md index b6793cf80f..798536ed13 100644 --- a/.github/skills/cuopt-routing-api-python/SKILL.md +++ b/.github/skills/cuopt-routing-api-python/SKILL.md @@ -1,11 +1,11 @@ --- name: cuopt-routing-api-python -description: Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use with cuopt-routing-common for concepts. Use when the user is building or solving routing in Python. +description: Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use with cuopt-routing-formulation for concepts. Use when the user is building or solving routing in Python. --- # cuOpt Routing — Python API -**Concepts:** Read `cuopt-routing-common/SKILL.md` for problem types, when to use, when to escalate. +**Concepts:** Read `cuopt-routing-formulation/SKILL.md` for problem types and formulation. This skill is **Python only**. Routing has no C API in cuOpt. @@ -96,4 +96,4 @@ ss.set_error_logging_mode(True) ## Escalate -See `cuopt-routing-common` for when to use cuopt-developer. +See `cuopt-routing-formulation` for when to use cuopt-developer. diff --git a/.github/skills/cuopt-routing-common/SKILL.md b/.github/skills/cuopt-routing-formulation/SKILL.md similarity index 86% rename from .github/skills/cuopt-routing-common/SKILL.md rename to .github/skills/cuopt-routing-formulation/SKILL.md index b906e91618..6ab87494ec 100644 --- a/.github/skills/cuopt-routing-common/SKILL.md +++ b/.github/skills/cuopt-routing-formulation/SKILL.md @@ -1,9 +1,9 @@ --- -name: cuopt-routing-common -description: Vehicle routing (VRP, TSP, PDP) with cuOpt — problem types and data requirements only. Domain concepts; no API or interface guidance. +name: cuopt-routing-formulation +description: Vehicle routing (VRP, TSP, PDP) with cuOpt — problem types and data requirements. Domain concepts; no API or interface. Use before coding with cuopt-routing-api-python. --- -# cuOpt Routing (common) +# cuOpt Routing Formulation Domain concepts for vehicle routing. No API or interface details here. From 68266d4a7919cc98bcbd49e361617b49091f6d26 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 27 Feb 2026 15:41:45 -0600 Subject: [PATCH 06/35] Adding assets to qp, routing lp --- .github/skills/cuopt-lp-milp-api-c/SKILL.md | 1 + .../cuopt-lp-milp-api-c/assets/README.md | 15 ++++ .../assets/lp_basic/README.md | 15 ++++ .../assets/lp_basic/lp_simple.c | 75 +++++++++++++++++++ .../assets/milp_basic/README.md | 12 +++ .../assets/milp_basic/milp_simple.c | 72 ++++++++++++++++++ .github/skills/cuopt-lp-milp-api-cli/SKILL.md | 1 + .../cuopt-lp-milp-api-cli/assets/README.md | 11 +++ .../assets/lp_production/README.md | 5 ++ .../assets/lp_production/production.mps | 16 ++++ .../assets/lp_simple/README.md | 5 ++ .../assets/lp_simple/sample.mps | 19 +++++ .../assets/milp_facility/README.md | 5 ++ .../assets/milp_facility/facility.mps | 27 +++++++ .github/skills/cuopt-qp-api-c/SKILL.md | 2 + .../skills/cuopt-qp-api-c/assets/README.md | 15 ++++ .github/skills/cuopt-qp-api-cli/SKILL.md | 2 + .../skills/cuopt-qp-api-cli/assets/README.md | 15 ++++ .github/skills/cuopt-qp-api-python/SKILL.md | 1 + .../cuopt-qp-api-python/assets/README.md | 11 +++ .../assets/least_squares/README.md | 5 ++ .../assets/least_squares/model.py | 23 ++++++ .../assets/maximization_workaround/README.md | 5 ++ .../assets/maximization_workaround/model.py | 21 ++++++ .../assets/portfolio/README.md | 7 ++ .../assets/portfolio/model.py | 40 ++++++++++ .../skills/cuopt-routing-api-python/SKILL.md | 1 + .../cuopt-routing-api-python/assets/README.md | 10 +++ .../assets/pdp_basic/README.md | 7 ++ .../assets/pdp_basic/model.py | 52 +++++++++++++ .../assets/vrp_basic/README.md | 7 ++ .../assets/vrp_basic/model.py | 27 +++++++ 32 files changed, 530 insertions(+) create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c create mode 100644 .github/skills/cuopt-lp-milp-api-cli/assets/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps create mode 100644 .github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps create mode 100644 .github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps create mode 100644 .github/skills/cuopt-qp-api-c/assets/README.md create mode 100644 .github/skills/cuopt-qp-api-cli/assets/README.md create mode 100644 .github/skills/cuopt-qp-api-python/assets/README.md create mode 100644 .github/skills/cuopt-qp-api-python/assets/least_squares/README.md create mode 100644 .github/skills/cuopt-qp-api-python/assets/least_squares/model.py create mode 100644 .github/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md create mode 100644 .github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py create mode 100644 .github/skills/cuopt-qp-api-python/assets/portfolio/README.md create mode 100644 .github/skills/cuopt-qp-api-python/assets/portfolio/model.py create mode 100644 .github/skills/cuopt-routing-api-python/assets/README.md create mode 100644 .github/skills/cuopt-routing-api-python/assets/pdp_basic/README.md create mode 100644 .github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py create mode 100644 .github/skills/cuopt-routing-api-python/assets/vrp_basic/README.md create mode 100644 .github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py diff --git a/.github/skills/cuopt-lp-milp-api-c/SKILL.md b/.github/skills/cuopt-lp-milp-api-c/SKILL.md index bc54cfe91a..5b77046fca 100644 --- a/.github/skills/cuopt-lp-milp-api-c/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-c/SKILL.md @@ -41,6 +41,7 @@ cuOptGetObjectiveValue(solution, &obj_value); ## Examples - [examples.md](resources/examples.md) — LP/MILP with build instructions +- **Reference code:** This skill's `assets/` — [lp_basic](assets/lp_basic/) (LP), [milp_basic](assets/milp_basic/) (MILP). See [assets/README.md](assets/README.md) for build commands. For **CLI** (MPS files), use `cuopt-lp-milp-api-cli`. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/README.md new file mode 100644 index 0000000000..63838ff3d2 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/README.md @@ -0,0 +1,15 @@ +# Assets — reference C examples + +LP/MILP C API reference implementations. Use as reference when building new applications; do not edit in place. Build requires cuOpt installed (see `cuopt-installation-api-c`). + +| Example | Type | Description | +|---------|------|-------------| +| [lp_basic](lp_basic/) | LP | Simple LP: create problem, solve, get solution | +| [milp_basic](milp_basic/) | MILP | Simple MILP with integer variable | + +Build (after setting `INCLUDE_PATH` and `LIB_PATH` to cuOpt): + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o lp_basic/lp_simple lp_basic/lp_simple.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./lp_basic/lp_simple +``` diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md new file mode 100644 index 0000000000..010666240f --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md @@ -0,0 +1,15 @@ +# Simple LP (C API) + +Minimize `-0.2*x1 + 0.1*x2` subject to: +- `3*x1 + 4*x2 <= 5.4` +- `2.7*x1 + 10.1*x2 <= 4.9` +- `x1, x2 >= 0` + +**Build:** From repo root or skill dir, with cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o lp_simple lp_simple.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./lp_simple +``` + +**See also:** [resources/examples.md](../../resources/examples.md) for parameter constants and more examples. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c b/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c new file mode 100644 index 0000000000..60d5858001 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c @@ -0,0 +1,75 @@ +/* + * Simple LP (C API): minimize -0.2*x1 + 0.1*x2 + * subject to 3*x1 + 4*x2 <= 5.4, 2.7*x1 + 10.1*x2 <= 4.9, x1,x2 >= 0 + */ +#include +#include +#include +#include + +int main(void) { + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 2; + + cuopt_int_t row_offsets[] = {0, 2, 4}; + cuopt_int_t column_indices[] = {0, 1, 0, 1}; + cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; + + cuopt_float_t objective_coefficients[] = {-0.2, 0.1}; + cuopt_float_t constraint_upper_bounds[] = {5.4, 4.9}; + cuopt_float_t constraint_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY}; + + cuopt_float_t var_lower_bounds[] = {0.0, 0.0}; + cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + cuopt_int_t status = cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, 0.0, + objective_coefficients, + row_offsets, column_indices, values, + constraint_lower_bounds, constraint_upper_bounds, + var_lower_bounds, var_upper_bounds, + variable_types, &problem + ); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + return 1; + } + + cuOptCreateSolverSettings(&settings); + cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + cuopt_float_t time, objective_value; + cuopt_int_t termination_status; + cuOptGetSolveTime(solution, &time); + cuOptGetTerminationStatus(solution, &termination_status); + cuOptGetObjectiveValue(solution, &objective_value); + + printf("Status: %d\n", termination_status); + printf("Time: %f s\n", time); + printf("Objective: %f\n", objective_value); + + cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (sol) { + cuOptGetPrimalSolution(solution, sol); + printf("x1 = %f, x2 = %f\n", sol[0], sol[1]); + free(sol); + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md new file mode 100644 index 0000000000..e3faa7a26e --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md @@ -0,0 +1,12 @@ +# Simple MILP (C API) + +Same as LP but `x1` is integer. Demonstrates variable types and MIP parameters. + +**Build:** With cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o milp_simple milp_simple.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./milp_simple +``` + +**See also:** [resources/examples.md](../../resources/examples.md) for full parameter reference. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c b/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c new file mode 100644 index 0000000000..b5741226e8 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c @@ -0,0 +1,72 @@ +/* + * Simple MILP (C API): same as LP but x1 is integer + */ +#include +#include +#include +#include + +int main(void) { + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 2; + + cuopt_int_t row_offsets[] = {0, 2, 4}; + cuopt_int_t column_indices[] = {0, 1, 0, 1}; + cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; + + cuopt_float_t objective_coefficients[] = {-0.2, 0.1}; + cuopt_float_t constraint_upper[] = {5.4, 4.9}; + cuopt_float_t constraint_lower[] = {-CUOPT_INFINITY, -CUOPT_INFINITY}; + cuopt_float_t var_lower[] = {0.0, 0.0}; + cuopt_float_t var_upper[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + + /* x1 = INTEGER, x2 = CONTINUOUS */ + char variable_types[] = {CUOPT_INTEGER, CUOPT_CONTINUOUS}; + + cuopt_int_t status = cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, 0.0, + objective_coefficients, + row_offsets, column_indices, values, + constraint_lower, constraint_upper, + var_lower, var_upper, + variable_types, &problem + ); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + return 1; + } + + cuOptCreateSolverSettings(&settings); + cuOptSetFloatParameter(settings, CUOPT_MIP_ABSOLUTE_TOLERANCE, 0.0001); + cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 120.0); + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + if (solution != NULL) { + cuopt_float_t objective_value; + cuOptGetObjectiveValue(solution, &objective_value); + printf("Objective: %f\n", objective_value); + + cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (sol) { + cuOptGetPrimalSolution(solution, sol); + printf("x1 (integer) = %f, x2 (continuous) = %f\n", sol[0], sol[1]); + free(sol); + } + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md index d1064ef5df..850c7e558e 100644 --- a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md @@ -51,6 +51,7 @@ Integer variables: use `'MARKER' 'INTORG'` before and `'MARKER' 'INTEND'` after ## Examples - [examples.md](resources/examples.md) — LP and MILP MPS examples, format reference, troubleshooting +- **Sample MPS files:** This skill's `assets/` — [lp_simple](assets/lp_simple/), [lp_production](assets/lp_production/), [milp_facility](assets/milp_facility/). See [assets/README.md](assets/README.md). ## Getting the CLI diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/README.md b/.github/skills/cuopt-lp-milp-api-cli/assets/README.md new file mode 100644 index 0000000000..b4e551b1ab --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/README.md @@ -0,0 +1,11 @@ +# Assets — sample MPS files + +Sample MPS files for use with `cuopt_cli`. Use as reference; do not edit in place. + +| File | Type | Description | +|------|------|-------------| +| [lp_production](lp_production/) | LP | Production planning: chairs + tables, wood/labor | +| [milp_facility](milp_facility/) | MILP | Facility location with binary open/close | +| [lp_simple](lp_simple/) | LP | Minimal LP (PROD_X, PROD_Y, two constraints) | + +**Run:** From each subdir or with path: `cuopt_cli lp_simple/sample.mps` (or `cuopt_cli production.mps`, etc.). See [resources/examples.md](../resources/examples.md) for options (`--time-limit`, `--mip-relative-tolerance`, etc.). diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md b/.github/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md new file mode 100644 index 0000000000..de4ca53043 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md @@ -0,0 +1,5 @@ +# Production LP (MPS) + +Production planning: maximize 40*chairs + 30*tables subject to wood and labor limits. + +**Run:** `cuopt_cli production.mps` or `cuopt_cli production.mps --time-limit 30` diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps b/.github/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps new file mode 100644 index 0000000000..40e3217b52 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps @@ -0,0 +1,16 @@ +NAME PRODUCTION +ROWS + N PROFIT + L WOOD + L LABOR +COLUMNS + CHAIRS PROFIT -40.0 + CHAIRS WOOD 2.0 + CHAIRS LABOR 4.0 + TABLES PROFIT -30.0 + TABLES WOOD 3.0 + TABLES LABOR 2.0 +RHS + RHS1 WOOD 240.0 + RHS1 LABOR 200.0 +ENDATA diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md b/.github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md new file mode 100644 index 0000000000..ed39464a77 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md @@ -0,0 +1,5 @@ +# Minimal LP (MPS) + +Maximize 40*PROD_X + 30*PROD_Y subject to resource constraints. Two variables, two constraints. + +**Run:** `cuopt_cli sample.mps` or `cuopt_cli sample.mps --time-limit 30` diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps b/.github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps new file mode 100644 index 0000000000..6baeb6e524 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps @@ -0,0 +1,19 @@ +NAME PRODUCTION_LP +ROWS + N PROFIT + L RES_A + L RES_B +COLUMNS + PROD_X PROFIT -40.0 + PROD_X RES_A 2.0 + PROD_X RES_B 4.0 + PROD_Y PROFIT -30.0 + PROD_Y RES_A 3.0 + PROD_Y RES_B 2.0 +RHS + RHS1 RES_A 120.0 + RHS1 RES_B 100.0 +BOUNDS + LO BND1 PROD_X 0.0 + LO BND1 PROD_Y 0.0 +ENDATA diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md b/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md new file mode 100644 index 0000000000..ac2a323908 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md @@ -0,0 +1,5 @@ +# Facility location MILP (MPS) + +Facility location with binary open/close variables. Integer markers: INTORG / INTEND. + +**Run:** `cuopt_cli facility.mps --time-limit 60 --mip-relative-tolerance 0.01` diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps b/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps new file mode 100644 index 0000000000..8640ff8c4f --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps @@ -0,0 +1,27 @@ +NAME FACILITY +ROWS + N COST + G DEMAND1 + L CAP1 + L CAP2 +COLUMNS + MARKER 'MARKER' 'INTORG' + OPEN1 COST 100.0 + OPEN1 CAP1 50.0 + OPEN2 COST 150.0 + OPEN2 CAP2 70.0 + MARKER 'MARKER' 'INTEND' + SHIP11 COST 5.0 + SHIP11 DEMAND1 1.0 + SHIP11 CAP1 -1.0 + SHIP21 COST 7.0 + SHIP21 DEMAND1 1.0 + SHIP21 CAP2 -1.0 +RHS + RHS1 DEMAND1 30.0 +BOUNDS + BV BND1 OPEN1 + BV BND1 OPEN2 + LO BND1 SHIP11 0.0 + LO BND1 SHIP21 0.0 +ENDATA diff --git a/.github/skills/cuopt-qp-api-c/SKILL.md b/.github/skills/cuopt-qp-api-c/SKILL.md index 3d82e82126..f107a60cbf 100644 --- a/.github/skills/cuopt-qp-api-c/SKILL.md +++ b/.github/skills/cuopt-qp-api-c/SKILL.md @@ -11,6 +11,8 @@ This skill is **C only**. For Python (beta), use `cuopt-qp-api-python`. QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic objectives. See LP/MILP C API docs and build instructions in `cuopt-lp-milp-api-c` for the base setup; then use the QP-specific creation/solve calls from the cuOpt C headers. +**Reference:** This skill's [assets/README.md](assets/README.md) — build pattern and pointers to LP/MILP C examples and repo QP docs. + ## Escalate See `cuopt-qp-formulation` for when to use cuopt-lp-milp-formulation or cuopt-developer. diff --git a/.github/skills/cuopt-qp-api-c/assets/README.md b/.github/skills/cuopt-qp-api-c/assets/README.md new file mode 100644 index 0000000000..05af93ab91 --- /dev/null +++ b/.github/skills/cuopt-qp-api-c/assets/README.md @@ -0,0 +1,15 @@ +# Assets — QP C API reference + +QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic objectives. + +**Build and run:** Use the same include/lib paths and link steps as in `cuopt-lp-milp-api-c` (see that skill's `assets/` and `resources/examples.md`). Then use QP-specific creation and solve calls from the cuOpt C headers. + +**Reference locations (in repo):** + +| Resource | Description | +|----------|-------------| +| `cuopt-lp-milp-api-c/assets/` | LP/MILP C examples and build pattern | +| `cuopt-lp-milp-api-c/resources/examples.md` | Parameter constants, CSR format | +| Repo docs | `docs/cuopt/source/cuopt-c/lp-qp-milp/` for QP C API and examples | + +No standalone QP C source files are included in this skill; copy the build pattern from LP/MILP and adapt for quadratic objective APIs from the headers. diff --git a/.github/skills/cuopt-qp-api-cli/SKILL.md b/.github/skills/cuopt-qp-api-cli/SKILL.md index aef0b82527..8a2731e93b 100644 --- a/.github/skills/cuopt-qp-api-cli/SKILL.md +++ b/.github/skills/cuopt-qp-api-cli/SKILL.md @@ -27,6 +27,8 @@ cuopt_cli problem.mps --time-limit 60 Check `cuopt_cli --help` and the repository documentation (e.g. `docs/cuopt/source/cuopt-cli/`) for QP file format and any QP-specific flags. +**Reference:** This skill's [assets/README.md](assets/README.md) — pointers to LP/MILP CLI samples and options. + ## Getting the CLI CLI is included with the Python package (`cuopt`). Install via pip or conda (see `cuopt-installation-api-python`); then run `cuopt_cli --help` to verify. diff --git a/.github/skills/cuopt-qp-api-cli/assets/README.md b/.github/skills/cuopt-qp-api-cli/assets/README.md new file mode 100644 index 0000000000..f59f06233b --- /dev/null +++ b/.github/skills/cuopt-qp-api-cli/assets/README.md @@ -0,0 +1,15 @@ +# Assets — QP CLI reference + +QP can be solved via `cuopt_cli` when the input format supports quadratic objectives (see repo docs and `cuopt_cli --help` for QP-specific options and file format). + +**Important:** QP objectives must be **minimization**. For maximization, negate the objective. + +**Reference:** + +| Resource | Description | +|----------|-------------| +| `cuopt-lp-milp-api-cli/assets/` | Sample MPS files and CLI usage for LP/MILP | +| `cuopt-lp-milp-api-cli/resources/examples.md` | CLI options (time limit, tolerances) | +| Repo docs | `docs/cuopt/source/cuopt-cli/` for QP file format and flags | + +No sample QP input files are included here; use LP/MILP assets as the CLI pattern and check documentation for quadratic term format. diff --git a/.github/skills/cuopt-qp-api-python/SKILL.md b/.github/skills/cuopt-qp-api-python/SKILL.md index c1fad47bf8..34bcb21993 100644 --- a/.github/skills/cuopt-qp-api-python/SKILL.md +++ b/.github/skills/cuopt-qp-api-python/SKILL.md @@ -52,6 +52,7 @@ if problem.Status.name in ["Optimal", "PrimalFeasible"]: ## Examples - [examples.md](resources/examples.md) — portfolio, least squares, maximization workaround +- **Reference models:** This skill's `assets/` — [portfolio](assets/portfolio/), [least_squares](assets/least_squares/), [maximization_workaround](assets/maximization_workaround/). See [assets/README.md](assets/README.md). ## Escalate diff --git a/.github/skills/cuopt-qp-api-python/assets/README.md b/.github/skills/cuopt-qp-api-python/assets/README.md new file mode 100644 index 0000000000..3c696f07b6 --- /dev/null +++ b/.github/skills/cuopt-qp-api-python/assets/README.md @@ -0,0 +1,11 @@ +# Assets — reference QP models + +QP reference implementations (Python, beta). Use as reference when building new applications; do not edit in place. + +| Model | Description | +|-------|-------------| +| [portfolio](portfolio/) | Minimize portfolio variance; budget and min-return constraints | +| [least_squares](least_squares/) | Minimize (x-3)² + (y-4)² (closest point) | +| [maximization_workaround](maximization_workaround/) | Maximize quadratic via minimize -f(x) | + +**Run:** From each subdir, `python model.py`. QP is **beta** and supports **MINIMIZE** only. See [resources/examples.md](../resources/examples.md) for more. diff --git a/.github/skills/cuopt-qp-api-python/assets/least_squares/README.md b/.github/skills/cuopt-qp-api-python/assets/least_squares/README.md new file mode 100644 index 0000000000..5592ff2ac0 --- /dev/null +++ b/.github/skills/cuopt-qp-api-python/assets/least_squares/README.md @@ -0,0 +1,5 @@ +# Least squares (QP) + +Minimize (x-3)² + (y-4)² — find point closest to (3, 4). Unconstrained quadratic. + +**Run:** `python model.py` diff --git a/.github/skills/cuopt-qp-api-python/assets/least_squares/model.py b/.github/skills/cuopt-qp-api-python/assets/least_squares/model.py new file mode 100644 index 0000000000..5361915e85 --- /dev/null +++ b/.github/skills/cuopt-qp-api-python/assets/least_squares/model.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Least squares: minimize (x-3)² + (y-4)². Solution should be x=3, y=4. +""" +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("LeastSquares") + +x = problem.addVariable(lb=-100, ub=100, vtype=CONTINUOUS, name="x") +y = problem.addVariable(lb=-100, ub=100, vtype=CONTINUOUS, name="y") + +problem.setObjective(x * x + y * y - 6 * x - 8 * y + 25, sense=MINIMIZE) + +problem.solve(SolverSettings()) + +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"x = {x.getValue():.4f}") + print(f"y = {y.getValue():.4f}") +else: + print(f"Status: {problem.Status.name}") diff --git a/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md b/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md new file mode 100644 index 0000000000..bcd0f2c3c1 --- /dev/null +++ b/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md @@ -0,0 +1,5 @@ +# Maximization workaround (QP) + +QP supports MINIMIZE only. To maximize f(x), minimize -f(x); then negate the optimal value. + +**Run:** `python model.py` diff --git a/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py b/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py new file mode 100644 index 0000000000..09d1e35d1a --- /dev/null +++ b/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Maximize -x² + 4x (max at x=2) by minimizing x² - 4x; then report -objective. +""" +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE + +problem = Problem("MaxWorkaround") + +x = problem.addVariable(lb=0, ub=10, vtype=CONTINUOUS, name="x") +problem.setObjective(x * x - 4 * x, sense=MINIMIZE) + +problem.solve() + +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"x = {x.getValue():.4f}") + print(f"Minimized value = {problem.ObjValue:.4f}") + print(f"Original maximum = {-problem.ObjValue:.4f}") +else: + print(f"Status: {problem.Status.name}") diff --git a/.github/skills/cuopt-qp-api-python/assets/portfolio/README.md b/.github/skills/cuopt-qp-api-python/assets/portfolio/README.md new file mode 100644 index 0000000000..cf2173a455 --- /dev/null +++ b/.github/skills/cuopt-qp-api-python/assets/portfolio/README.md @@ -0,0 +1,7 @@ +# Portfolio optimization (QP) + +Minimize portfolio variance (risk) subject to fully invested (sum x = 1) and minimum return. Three assets; Q must be PSD. + +**Run:** `python model.py` + +**Note:** QP is beta; objective must be MINIMIZE. diff --git a/.github/skills/cuopt-qp-api-python/assets/portfolio/model.py b/.github/skills/cuopt-qp-api-python/assets/portfolio/model.py new file mode 100644 index 0000000000..d89742a29e --- /dev/null +++ b/.github/skills/cuopt-qp-api-python/assets/portfolio/model.py @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Portfolio: minimize variance x'Qx subject to sum(x)=1, r'x >= target, x >= 0. +QP is beta; MUST use MINIMIZE. +""" +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("Portfolio") + +x1 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_a") +x2 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_b") +x3 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_c") + +r1, r2, r3 = 0.12, 0.08, 0.05 +target_return = 0.08 + +problem.setObjective( + 0.04 * x1 * x1 + 0.02 * x2 * x2 + 0.01 * x3 * x3 + + 0.02 * x1 * x2 + 0.01 * x1 * x3 + 0.016 * x2 * x3, + sense=MINIMIZE, +) +problem.addConstraint(x1 + x2 + x3 == 1, name="budget") +problem.addConstraint(r1 * x1 + r2 * x2 + r3 * x3 >= target_return, name="min_return") + +settings = SolverSettings() +settings.set_parameter("time_limit", 60) +problem.solve(settings) + +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Portfolio variance: {problem.ObjValue:.6f}") + print(f"Std dev: {problem.ObjValue**0.5:.4f}") + print(f" Stock A: {x1.getValue()*100:.2f}%") + print(f" Stock B: {x2.getValue()*100:.2f}%") + print(f" Stock C: {x3.getValue()*100:.2f}%") + print(f"Expected return: {(r1*x1.getValue()+r2*x2.getValue()+r3*x3.getValue())*100:.2f}%") +else: + print(f"Status: {problem.Status.name}") diff --git a/.github/skills/cuopt-routing-api-python/SKILL.md b/.github/skills/cuopt-routing-api-python/SKILL.md index 798536ed13..dfb5b573ec 100644 --- a/.github/skills/cuopt-routing-api-python/SKILL.md +++ b/.github/skills/cuopt-routing-api-python/SKILL.md @@ -93,6 +93,7 @@ ss.set_error_logging_mode(True) - [examples.md](resources/examples.md) — VRP, PDP, multi-depot - [server_examples.md](resources/server_examples.md) — REST client (curl, Python) +- **Reference models:** This skill's `assets/` — [vrp_basic](assets/vrp_basic/), [pdp_basic](assets/pdp_basic/). See [assets/README.md](assets/README.md). ## Escalate diff --git a/.github/skills/cuopt-routing-api-python/assets/README.md b/.github/skills/cuopt-routing-api-python/assets/README.md new file mode 100644 index 0000000000..6b7a8091c9 --- /dev/null +++ b/.github/skills/cuopt-routing-api-python/assets/README.md @@ -0,0 +1,10 @@ +# Assets — reference routing models + +Routing reference implementations (Python). Use as reference when building new applications; do not edit in place. + +| Model | Type | Description | +|-------|------|-------------| +| [vrp_basic](vrp_basic/) | VRP | Minimal VRP: 4 locations, 1 vehicle, 3 orders | +| [pdp_basic](pdp_basic/) | PDP | Pickup-delivery pairs, capacity dimension | + +**Run:** From each subdir, `python model.py` (requires cuOpt and cudf). See [resources/examples.md](../resources/examples.md) for more patterns (time windows, multi-depot). diff --git a/.github/skills/cuopt-routing-api-python/assets/pdp_basic/README.md b/.github/skills/cuopt-routing-api-python/assets/pdp_basic/README.md new file mode 100644 index 0000000000..64e345bb7c --- /dev/null +++ b/.github/skills/cuopt-routing-api-python/assets/pdp_basic/README.md @@ -0,0 +1,7 @@ +# Pickup-Delivery (PDP) + +2 pickup-delivery pairs (4 orders), 2 vehicles. Pickup must occur before delivery; capacity dimension. + +**Run:** `python model.py` + +**See also:** [resources/examples.md](../../resources/examples.md) for more PDP and VRP patterns. diff --git a/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py b/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py new file mode 100644 index 0000000000..0101375a8b --- /dev/null +++ b/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +PDP: 2 pickup-delivery pairs, 2 vehicles. Pickup before delivery; capacity dimension. +""" +import cudf +from cuopt import routing + +cost_matrix = cudf.DataFrame([ + [0, 10, 20, 30, 40], + [10, 0, 15, 25, 35], + [20, 15, 0, 10, 20], + [30, 25, 10, 0, 15], + [40, 35, 20, 15, 0], +], dtype="float32") + +transit_time_matrix = cost_matrix.copy(deep=True) +n_fleet = 2 +n_orders = 4 + +order_locations = cudf.Series([1, 2, 3, 4]) +pickup_indices = cudf.Series([0, 2]) +delivery_indices = cudf.Series([1, 3]) +demand = cudf.Series([10, -10, 15, -15], dtype="int32") +vehicle_capacity = cudf.Series([50, 50], dtype="int32") + +dm = routing.DataModel( + n_locations=cost_matrix.shape[0], + n_fleet=n_fleet, + n_orders=n_orders, +) +dm.add_cost_matrix(cost_matrix) +dm.add_transit_time_matrix(transit_time_matrix) +dm.set_order_locations(order_locations) +dm.add_capacity_dimension("load", demand, vehicle_capacity) +dm.set_pickup_delivery_pairs(pickup_indices, delivery_indices) +dm.set_vehicle_locations( + cudf.Series([0, 0], dtype="int32"), + cudf.Series([0, 0], dtype="int32"), +) + +ss = routing.SolverSettings() +ss.set_time_limit(10) +solution = routing.Solve(dm, ss) + +print(f"Status: {solution.get_status()}") +if solution.get_status() == 0: + solution.display_routes() + print(f"Total cost: {solution.get_total_objective()}") +else: + print(solution.get_error_message()) diff --git a/.github/skills/cuopt-routing-api-python/assets/vrp_basic/README.md b/.github/skills/cuopt-routing-api-python/assets/vrp_basic/README.md new file mode 100644 index 0000000000..cdb2890269 --- /dev/null +++ b/.github/skills/cuopt-routing-api-python/assets/vrp_basic/README.md @@ -0,0 +1,7 @@ +# Minimal VRP + +4 locations (depot 0 + 3 customers), 1 vehicle, 3 orders. Cost matrix only; no time windows or capacity. + +**Run:** `python model.py` + +**See also:** [resources/examples.md](../../resources/examples.md) for VRP with time windows, capacity, and multi-depot. diff --git a/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py b/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py new file mode 100644 index 0000000000..f89e3d3a97 --- /dev/null +++ b/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Minimal VRP: 4 locations, 1 vehicle, 3 orders. Cost matrix only. +""" +import cudf +from cuopt import routing + +cost_matrix = cudf.DataFrame([ + [0, 10, 15, 20], + [10, 0, 12, 18], + [15, 12, 0, 10], + [20, 18, 10, 0], +], dtype="float32") + +dm = routing.DataModel(n_locations=4, n_fleet=1, n_orders=3) +dm.add_cost_matrix(cost_matrix) +dm.set_order_locations(cudf.Series([1, 2, 3])) + +solution = routing.Solve(dm, routing.SolverSettings()) + +if solution.get_status() == 0: + solution.display_routes() + print(f"Total cost: {solution.get_total_objective()}") +else: + print(f"Status: {solution.get_status()}", solution.get_error_message()) From f7d3c59ab1bc7411f4d0f1ca9ec30a37e8098984 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 27 Feb 2026 15:46:06 -0600 Subject: [PATCH 07/35] removing cross reference to skill --- .../skills/cuopt-installation-developer/SKILL.md | 6 +++--- .github/skills/cuopt-lp-milp-api-c/SKILL.md | 10 +++++----- .../skills/cuopt-lp-milp-api-c/assets/README.md | 2 +- .github/skills/cuopt-lp-milp-api-cli/SKILL.md | 8 ++++---- .github/skills/cuopt-lp-milp-api-python/SKILL.md | 2 +- .github/skills/cuopt-lp-milp-formulation/SKILL.md | 4 ++-- .github/skills/cuopt-qp-api-c/SKILL.md | 12 ++++++------ .github/skills/cuopt-qp-api-c/assets/README.md | 12 +++--------- .github/skills/cuopt-qp-api-cli/SKILL.md | 14 ++++++-------- .github/skills/cuopt-qp-api-cli/assets/README.md | 10 ++-------- .github/skills/cuopt-qp-api-python/SKILL.md | 8 ++++---- .github/skills/cuopt-qp-formulation/SKILL.md | 2 +- .github/skills/cuopt-routing-api-python/SKILL.md | 6 +++--- .github/skills/cuopt-routing-formulation/SKILL.md | 2 +- .github/skills/cuopt-server-api-python/SKILL.md | 6 ++---- .github/skills/cuopt-user-rules/SKILL.md | 2 +- 16 files changed, 45 insertions(+), 61 deletions(-) diff --git a/.github/skills/cuopt-installation-developer/SKILL.md b/.github/skills/cuopt-installation-developer/SKILL.md index ddfbe6afaa..c42cac60b5 100644 --- a/.github/skills/cuopt-installation-developer/SKILL.md +++ b/.github/skills/cuopt-installation-developer/SKILL.md @@ -1,16 +1,16 @@ --- name: cuopt-installation-developer -description: Developer installation — build cuOpt from source, run tests. Use when the user wants to set up a dev environment to contribute or modify cuOpt. For contribution rules and PRs, use cuopt-developer after setup. +description: Developer installation — build cuOpt from source, run tests. Use when the user wants to set up a dev environment to contribute or modify cuOpt. --- # cuOpt Installation — Developer -Set up an environment to **build cuOpt from source** and run tests. For contribution behavior and PRs, use **cuopt-developer** after the build works. +Set up an environment to **build cuOpt from source** and run tests. For contribution behavior and PRs, see the developer skill after the build works. ## When to use this skill - User wants to *build* cuOpt (clone, build deps, build, tests). -- Not for *using* cuOpt (pip/conda) — use `cuopt-installation-api-python` or `cuopt-installation-api-c` instead. +- Not for *using* cuOpt (pip/conda) — use the user installation skill instead. ## Required questions (environment) diff --git a/.github/skills/cuopt-lp-milp-api-c/SKILL.md b/.github/skills/cuopt-lp-milp-api-c/SKILL.md index 5b77046fca..01c458399e 100644 --- a/.github/skills/cuopt-lp-milp-api-c/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-c/SKILL.md @@ -1,13 +1,13 @@ --- name: cuopt-lp-milp-api-c -description: LP and MILP with cuOpt — C API only. Use with cuopt-lp-milp-formulation for concepts. Use when the user is embedding LP/MILP in C/C++. +description: LP and MILP with cuOpt — C API only. Use when the user is embedding LP/MILP in C/C++. --- # cuOpt LP/MILP — C API -**Concepts:** Read `cuopt-lp-milp-formulation/SKILL.md` for problem type and formulation. +Confirm problem type and formulation (variables, objective, constraints, variable types) before coding. -This skill is **C only**. For Python, use `cuopt-lp-milp-api-python`. +This skill is **C only**. ## Quick Reference: C API @@ -43,8 +43,8 @@ cuOptGetObjectiveValue(solution, &obj_value); - [examples.md](resources/examples.md) — LP/MILP with build instructions - **Reference code:** This skill's `assets/` — [lp_basic](assets/lp_basic/) (LP), [milp_basic](assets/milp_basic/) (MILP). See [assets/README.md](assets/README.md) for build commands. -For **CLI** (MPS files), use `cuopt-lp-milp-api-cli`. +For **CLI** (MPS files), see the LP/MILP CLI skill. ## Escalate -See `cuopt-lp-milp-formulation` for when to use cuopt-qp-formulation or cuopt-developer. +If the problem is quadratic (squared or cross terms in the objective), use QP. For contribution or build-from-source, see the developer skill. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/README.md index 63838ff3d2..a92e64495f 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/README.md +++ b/.github/skills/cuopt-lp-milp-api-c/assets/README.md @@ -1,6 +1,6 @@ # Assets — reference C examples -LP/MILP C API reference implementations. Use as reference when building new applications; do not edit in place. Build requires cuOpt installed (see `cuopt-installation-api-c`). +LP/MILP C API reference implementations. Use as reference when building new applications; do not edit in place. Build requires cuOpt installed (include and lib paths set). | Example | Type | Description | |---------|------|-------------| diff --git a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md index 850c7e558e..8eefea4eb1 100644 --- a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md @@ -1,13 +1,13 @@ --- name: cuopt-lp-milp-api-cli -description: LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use with cuopt-lp-milp-formulation for concepts. Use when the user is solving from MPS via command line. +description: LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use when the user is solving from MPS via command line. --- # cuOpt LP/MILP — CLI -**Concepts:** Read `cuopt-lp-milp-formulation/SKILL.md` for problem type and formulation. +Confirm problem type and formulation (variables, objective, constraints, variable types) before coding. -This skill is **CLI only**. For Python or C, use `cuopt-lp-milp-api-python` or `cuopt-lp-milp-api-c`. +This skill is **CLI only** (MPS input). ## Basic usage @@ -55,4 +55,4 @@ Integer variables: use `'MARKER' 'INTORG'` before and `'MARKER' 'INTEND'` after ## Getting the CLI -CLI is included with the Python package (`cuopt`). Install via pip or conda (see `cuopt-installation-common` + `cuopt-installation-api-python`); then run `cuopt_cli --help` to verify. +CLI is included with the Python package (`cuopt`). Install via pip or conda; then run `cuopt_cli --help` to verify. diff --git a/.github/skills/cuopt-lp-milp-api-python/SKILL.md b/.github/skills/cuopt-lp-milp-api-python/SKILL.md index fb771b5e67..272c2c5423 100644 --- a/.github/skills/cuopt-lp-milp-api-python/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-python/SKILL.md @@ -9,7 +9,7 @@ Model and solve linear and mixed-integer linear programs using NVIDIA cuOpt's GP ## Before You Start -Use the summary from **cuopt-lp-milp-formulation** (parameters, constraints, decisions, objective) if available; otherwise ask for decision variables, objective, and constraints. Then confirm **variable types** (see below) and **interface** (Python API recommended). +Use a formulation summary (parameters, constraints, decisions, objective) if available; otherwise ask for decision variables, objective, and constraints. Then confirm **variable types** (see below) and **interface** (Python API recommended). ## Choosing LP vs MILP diff --git a/.github/skills/cuopt-lp-milp-formulation/SKILL.md b/.github/skills/cuopt-lp-milp-formulation/SKILL.md index a482a01f06..9b4a1d8b9d 100644 --- a/.github/skills/cuopt-lp-milp-formulation/SKILL.md +++ b/.github/skills/cuopt-lp-milp-formulation/SKILL.md @@ -1,11 +1,11 @@ --- name: cuopt-lp-milp-formulation -description: LP/MILP concepts and going from problem text to formulation. What LP/MILP are, required formulation questions, typical modeling elements, and how to parse problem statements (parameters, constraints, decisions, objective). Use before coding with cuopt-lp-milp-api-python/c/cli. +description: LP/MILP concepts and going from problem text to formulation. What LP/MILP are, required formulation questions, typical modeling elements, and how to parse problem statements (parameters, constraints, decisions, objective). --- # cuOpt LP/MILP Formulation -Concepts and workflow for going from a problem description to a clear formulation. No API code here — use the API skills to implement. +Concepts and workflow for going from a problem description to a clear formulation. No API code here. ## What is LP / MILP diff --git a/.github/skills/cuopt-qp-api-c/SKILL.md b/.github/skills/cuopt-qp-api-c/SKILL.md index f107a60cbf..77a62069d4 100644 --- a/.github/skills/cuopt-qp-api-c/SKILL.md +++ b/.github/skills/cuopt-qp-api-c/SKILL.md @@ -1,18 +1,18 @@ --- name: cuopt-qp-api-c -description: Quadratic Programming (QP) with cuOpt — C API. Use with cuopt-qp-formulation for concepts. Use when the user is embedding QP in C/C++. +description: Quadratic Programming (QP) with cuOpt — C API. Use when the user is embedding QP in C/C++. --- # cuOpt QP — C API -**Concepts:** Read `cuopt-qp-formulation/SKILL.md` for when QP applies, minimize-only, when to escalate. +Confirm the objective has squared or cross terms (QP); if purely linear, use LP/MILP. QP must be minimization. -This skill is **C only**. For Python (beta), use `cuopt-qp-api-python`. +This skill is **C only**. -QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic objectives. See LP/MILP C API docs and build instructions in `cuopt-lp-milp-api-c` for the base setup; then use the QP-specific creation/solve calls from the cuOpt C headers. +QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic objectives. Use the same include/lib paths and build pattern as for LP/MILP C (see this skill's assets/README.md); then use the QP-specific creation/solve calls from the cuOpt C headers. -**Reference:** This skill's [assets/README.md](assets/README.md) — build pattern and pointers to LP/MILP C examples and repo QP docs. +**Reference:** This skill's [assets/README.md](assets/README.md) — build pattern and repo QP C API docs. ## Escalate -See `cuopt-qp-formulation` for when to use cuopt-lp-milp-formulation or cuopt-developer. +If the problem is linear, use LP/MILP. For contribution or build-from-source, see the developer skill. diff --git a/.github/skills/cuopt-qp-api-c/assets/README.md b/.github/skills/cuopt-qp-api-c/assets/README.md index 05af93ab91..b3fcea0586 100644 --- a/.github/skills/cuopt-qp-api-c/assets/README.md +++ b/.github/skills/cuopt-qp-api-c/assets/README.md @@ -2,14 +2,8 @@ QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic objectives. -**Build and run:** Use the same include/lib paths and link steps as in `cuopt-lp-milp-api-c` (see that skill's `assets/` and `resources/examples.md`). Then use QP-specific creation and solve calls from the cuOpt C headers. +**Build and run:** Use the same include/lib paths and link steps as for LP/MILP C (see repository documentation for build and examples). Then use the QP-specific creation and solve calls from the cuOpt C headers. -**Reference locations (in repo):** +**Repo docs:** `docs/cuopt/source/cuopt-c/lp-qp-milp/` for QP C API and examples; parameter constants and CSR format are in the same doc tree. -| Resource | Description | -|----------|-------------| -| `cuopt-lp-milp-api-c/assets/` | LP/MILP C examples and build pattern | -| `cuopt-lp-milp-api-c/resources/examples.md` | Parameter constants, CSR format | -| Repo docs | `docs/cuopt/source/cuopt-c/lp-qp-milp/` for QP C API and examples | - -No standalone QP C source files are included in this skill; copy the build pattern from LP/MILP and adapt for quadratic objective APIs from the headers. +No standalone QP C source files are included in this skill; adapt the LP/MILP C build pattern for quadratic objective APIs from the headers. diff --git a/.github/skills/cuopt-qp-api-cli/SKILL.md b/.github/skills/cuopt-qp-api-cli/SKILL.md index 8a2731e93b..6af0cce6b4 100644 --- a/.github/skills/cuopt-qp-api-cli/SKILL.md +++ b/.github/skills/cuopt-qp-api-cli/SKILL.md @@ -1,20 +1,18 @@ --- name: cuopt-qp-api-cli -description: QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use with cuopt-qp-formulation for concepts. Use when the user is solving QP from the command line. +description: QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use when the user is solving QP from the command line. --- # cuOpt QP — CLI -**Concepts:** Read `cuopt-qp-formulation/SKILL.md` for when QP applies and the **minimize-only** rule. +QP objectives must be **minimization**. For maximization, negate the objective. -This skill is **CLI only** for QP. For Python or C, use `cuopt-qp-api-python` or `cuopt-qp-api-c`. +This skill is **CLI only** for QP. ## QP via CLI cuOpt CLI supports QP (quadratic objectives). Use the same `cuopt_cli` tool; input format and options may extend the LP/MILP MPS workflow to allow quadratic terms (see repo docs or `cuopt_cli --help` for QP-specific options). -**Important:** QP objectives must be **minimization**. For maximization, negate the objective. - ## Basic usage ```bash @@ -27,12 +25,12 @@ cuopt_cli problem.mps --time-limit 60 Check `cuopt_cli --help` and the repository documentation (e.g. `docs/cuopt/source/cuopt-cli/`) for QP file format and any QP-specific flags. -**Reference:** This skill's [assets/README.md](assets/README.md) — pointers to LP/MILP CLI samples and options. +**Reference:** This skill's [assets/README.md](assets/README.md) — CLI options and repo docs. ## Getting the CLI -CLI is included with the Python package (`cuopt`). Install via pip or conda (see `cuopt-installation-api-python`); then run `cuopt_cli --help` to verify. +CLI is included with the Python package (`cuopt`). Install via pip or conda; then run `cuopt_cli --help` to verify. ## Escalate -See `cuopt-qp-formulation` for when to use cuopt-lp-milp-formulation or cuopt-developer. +If the problem is linear, use LP/MILP CLI. For contribution or build-from-source, see the developer skill. diff --git a/.github/skills/cuopt-qp-api-cli/assets/README.md b/.github/skills/cuopt-qp-api-cli/assets/README.md index f59f06233b..040f03efad 100644 --- a/.github/skills/cuopt-qp-api-cli/assets/README.md +++ b/.github/skills/cuopt-qp-api-cli/assets/README.md @@ -4,12 +4,6 @@ QP can be solved via `cuopt_cli` when the input format supports quadratic object **Important:** QP objectives must be **minimization**. For maximization, negate the objective. -**Reference:** +**Repo docs:** `docs/cuopt/source/cuopt-cli/` for QP file format and flags. For sample MPS files and CLI options (time limit, tolerances), see the repository documentation. -| Resource | Description | -|----------|-------------| -| `cuopt-lp-milp-api-cli/assets/` | Sample MPS files and CLI usage for LP/MILP | -| `cuopt-lp-milp-api-cli/resources/examples.md` | CLI options (time limit, tolerances) | -| Repo docs | `docs/cuopt/source/cuopt-cli/` for QP file format and flags | - -No sample QP input files are included here; use LP/MILP assets as the CLI pattern and check documentation for quadratic term format. +No sample QP input files are included here; check documentation for quadratic term format. diff --git a/.github/skills/cuopt-qp-api-python/SKILL.md b/.github/skills/cuopt-qp-api-python/SKILL.md index 34bcb21993..b3073fb2a9 100644 --- a/.github/skills/cuopt-qp-api-python/SKILL.md +++ b/.github/skills/cuopt-qp-api-python/SKILL.md @@ -1,13 +1,13 @@ --- name: cuopt-qp-api-python -description: Quadratic Programming (QP) with cuOpt — Python API only (beta). Use with cuopt-qp-formulation for concepts. Use when the user is building or solving QP in Python. +description: Quadratic Programming (QP) with cuOpt — Python API only (beta). Use when the user is building or solving QP in Python. --- # cuOpt QP — Python API (beta) -**Concepts:** Read `cuopt-qp-formulation/SKILL.md` for when QP applies, minimize-only, when to escalate. +Confirm the objective has squared or cross terms (QP); if purely linear, use LP/MILP. QP must be minimization. -This skill is **Python only**. For C, use `cuopt-qp-api-c`. **QP is beta.** +This skill is **Python only**. **QP is beta.** ## CRITICAL: MINIMIZE only @@ -56,4 +56,4 @@ if problem.Status.name in ["Optimal", "PrimalFeasible"]: ## Escalate -See `cuopt-qp-formulation` for when to use cuopt-lp-milp-formulation or cuopt-developer. +If the problem is linear (no squared or cross terms), use LP/MILP. For contribution or build-from-source, see the developer skill. diff --git a/.github/skills/cuopt-qp-formulation/SKILL.md b/.github/skills/cuopt-qp-formulation/SKILL.md index 84e88714be..8a1c535fe7 100644 --- a/.github/skills/cuopt-qp-formulation/SKILL.md +++ b/.github/skills/cuopt-qp-formulation/SKILL.md @@ -1,6 +1,6 @@ --- name: cuopt-qp-formulation -description: Quadratic Programming (QP) with cuOpt — problem form and constraints. Domain concepts; no API or interface. QP is beta. Use before coding with cuopt-qp-api-python/c/cli. +description: Quadratic Programming (QP) with cuOpt — problem form and constraints. Domain concepts; no API or interface. QP is beta. --- # cuOpt QP Formulation diff --git a/.github/skills/cuopt-routing-api-python/SKILL.md b/.github/skills/cuopt-routing-api-python/SKILL.md index dfb5b573ec..c1e1c9f24d 100644 --- a/.github/skills/cuopt-routing-api-python/SKILL.md +++ b/.github/skills/cuopt-routing-api-python/SKILL.md @@ -1,11 +1,11 @@ --- name: cuopt-routing-api-python -description: Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use with cuopt-routing-formulation for concepts. Use when the user is building or solving routing in Python. +description: Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use when the user is building or solving routing in Python. --- # cuOpt Routing — Python API -**Concepts:** Read `cuopt-routing-formulation/SKILL.md` for problem types and formulation. +Confirm problem type (TSP, VRP, PDP) and data (locations, orders, fleet, constraints) before coding. This skill is **Python only**. Routing has no C API in cuOpt. @@ -97,4 +97,4 @@ ss.set_error_logging_mode(True) ## Escalate -See `cuopt-routing-formulation` for when to use cuopt-developer. +For contribution or build-from-source, see the developer skill. diff --git a/.github/skills/cuopt-routing-formulation/SKILL.md b/.github/skills/cuopt-routing-formulation/SKILL.md index 6ab87494ec..e714437073 100644 --- a/.github/skills/cuopt-routing-formulation/SKILL.md +++ b/.github/skills/cuopt-routing-formulation/SKILL.md @@ -1,6 +1,6 @@ --- name: cuopt-routing-formulation -description: Vehicle routing (VRP, TSP, PDP) with cuOpt — problem types and data requirements. Domain concepts; no API or interface. Use before coding with cuopt-routing-api-python. +description: Vehicle routing (VRP, TSP, PDP) with cuOpt — problem types and data requirements. Domain concepts; no API or interface. --- # cuOpt Routing Formulation diff --git a/.github/skills/cuopt-server-api-python/SKILL.md b/.github/skills/cuopt-server-api-python/SKILL.md index 2fb4558817..abb7da1ea8 100644 --- a/.github/skills/cuopt-server-api-python/SKILL.md +++ b/.github/skills/cuopt-server-api-python/SKILL.md @@ -1,12 +1,10 @@ --- name: cuopt-server-api-python -description: cuOpt REST server — start server, endpoints, Python/curl client examples. Use with cuopt-server-common for capabilities and workflow. Use when the user is deploying or calling the REST API. +description: cuOpt REST server — start server, endpoints, Python/curl client examples. Use when the user is deploying or calling the REST API. --- # cuOpt Server — Deploy and client (Python/curl) -**Concepts:** Read `cuopt-server-common/SKILL.md` for capabilities, workflow, when to escalate. - This skill covers **starting the server** and **client examples** (curl, Python). Server has no separate C API (clients can be any language). ## Start server @@ -71,4 +69,4 @@ Use `travel_time_matrix_data` (not transit_time_matrix_data). Capacities: `[[50, ## Escalate -See `cuopt-server-common` for when to use cuopt-developer. +For contribution or build-from-source, see the developer skill. diff --git a/.github/skills/cuopt-user-rules/SKILL.md b/.github/skills/cuopt-user-rules/SKILL.md index 53169227b3..1b1732024d 100644 --- a/.github/skills/cuopt-user-rules/SKILL.md +++ b/.github/skills/cuopt-user-rules/SKILL.md @@ -104,7 +104,7 @@ Before generating code, **read the canonical example** for that problem type and | QP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_qp_example.py` | `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/` | | Server | `docs/cuopt/source/cuopt_spec.yaml` (OpenAPI) | — | -**Don't invent API patterns.** Copy from examples. For C, use the skill’s `resources/c_api_examples.md` when available. With the flat layout, use the api skill for the user's language (e.g. cuopt-lp-milp-api-python or cuopt-lp-milp-api-c); each has resources/examples.md or equivalent. +**Don't invent API patterns.** Copy from examples. For C, use the skill’s `resources/c_api_examples.md` when available. Use the API skill that matches the user's language (Python, C, or CLI); each has resources or examples in that skill. --- From 1e8c0347fb468654f90116a3c1d0836680d1bd2f Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 27 Feb 2026 16:01:28 -0600 Subject: [PATCH 08/35] update --- .github/skills/cuopt-user-rules/SKILL.md | 41 ++++++++---------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/.github/skills/cuopt-user-rules/SKILL.md b/.github/skills/cuopt-user-rules/SKILL.md index 1b1732024d..f23fdb19d5 100644 --- a/.github/skills/cuopt-user-rules/SKILL.md +++ b/.github/skills/cuopt-user-rules/SKILL.md @@ -9,14 +9,14 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before --- -## 1. Ask Before Assuming +## Ask Before Assuming **Always clarify ambiguous requirements before implementing:** -- What **language/interface**? (**Python** API / **C** API / REST Server / CLI) -- What problem type? (Routing / LP / MILP / QP) -- What constraints matter? (time windows, capacities, etc.) -- What output format? (solution values, routes, visualization) +- What **language/interface**? +- What problem type? +- What constraints matter? +- What output format? **Skip asking only if:** - User explicitly stated the requirement @@ -24,7 +24,7 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before --- -## 2. Handle Incomplete Questions +## Handle Incomplete Questions **If a question seems partial or incomplete, ask follow-up questions:** @@ -42,7 +42,7 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before --- -## 3. Clarify Data Requirements +## Clarify Data Requirements **Before generating examples, ask about data:** @@ -69,7 +69,7 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before --- -## 4. MUST Verify Understanding +## MUST Verify Understanding **Before writing substantial code, you MUST confirm your understanding:** @@ -84,7 +84,7 @@ Is this correct?" --- -## 5. Follow Requirements Exactly +## Follow Requirements Exactly - Use the **exact** variable names, formats, and structures the user specifies - Don't add features the user didn't ask for @@ -93,22 +93,7 @@ Is this correct?" --- -## 6. Read Examples First - -Before generating code, **read the canonical example** for that problem type and **language** (Python vs C): - -| Problem | Python | C | -|---------|--------|---| -| Routing | `docs/cuopt/source/cuopt-python/routing/examples/` | — (no C API) | -| LP/MILP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/` | `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/` | -| QP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_qp_example.py` | `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/` | -| Server | `docs/cuopt/source/cuopt_spec.yaml` (OpenAPI) | — | - -**Don't invent API patterns.** Copy from examples. For C, use the skill’s `resources/c_api_examples.md` when available. Use the API skill that matches the user's language (Python, C, or CLI); each has resources or examples in that skill. - ---- - -## 7. Check Results +## Check Results After providing a solution, guide the user to verify: @@ -120,7 +105,7 @@ Provide diagnostic code snippets when helpful. --- -## 8. Check Environment First +## Check Environment First **Before writing code or suggesting installation, verify the user's setup:** @@ -166,7 +151,7 @@ Provide diagnostic code snippets when helpful. --- -## 9. Ask Before Running +## Ask Before Running **Do not execute commands or code without explicit permission:** @@ -183,7 +168,7 @@ Provide diagnostic code snippets when helpful. --- -## 10. No Privileged Operations +## No Privileged Operations **Never do these without explicit user request AND confirmation:** From ebed0b883bcc85357daf33f88d54b6c4fa3fd8e0 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Sat, 28 Feb 2026 11:09:12 -0600 Subject: [PATCH 09/35] update --- .github/AGENTS.md | 13 ++++++++++++- .github/skills/cuopt-user-rules/SKILL.md | 23 ++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/.github/AGENTS.md b/.github/AGENTS.md index 4a577c10e9..8aaf1d4003 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -2,6 +2,10 @@ AI agent skills for NVIDIA cuOpt optimization engine. Skills use a **flat layout**: **common** (concepts) + **api-python** or **api-c** (implementation) per domain. +> **🔒 MANDATORY — Security:** You MUST NOT install, upgrade, or modify packages. Provide the exact command for the user to run; they execute it. No exceptions. + +> **🔒 MANDATORY — Ambiguity:** When the problem could be read more than one way, you MUST either **ask the user to clarify** or **solve every plausible interpretation and report all outcomes**. Never pick one interpretation silently. + ## Quick Start - **Using cuOpt** (routing, LP, QP, install, server): read `skills/cuopt-user-rules/`, then choose skills from the index below based on the user’s task, problem type, and interface (Python / C / CLI). @@ -30,7 +34,14 @@ Choose which skills to load from the index; ## Resources -- [cuOpt Documentation](https://docs.nvidia.com/cuopt/user-guide/latest/) +### Documentation +- [cuOpt User Guide](https://docs.nvidia.com/cuopt/user-guide/latest/introduction.html) +- [API Reference](https://docs.nvidia.com/cuopt/user-guide/latest/api.html) + +### Examples - [cuopt-examples repo](https://github.com/NVIDIA/cuopt-examples) +- [Google Colab notebooks](https://colab.research.google.com/github/nvidia/cuopt-examples/) + +### Support - [GitHub Issues](https://github.com/NVIDIA/cuopt/issues) - [Developer Forums](https://forums.developer.nvidia.com/c/ai-data-science/nvidia-cuopt/514) diff --git a/.github/skills/cuopt-user-rules/SKILL.md b/.github/skills/cuopt-user-rules/SKILL.md index f23fdb19d5..329078ee53 100644 --- a/.github/skills/cuopt-user-rules/SKILL.md +++ b/.github/skills/cuopt-user-rules/SKILL.md @@ -101,6 +101,15 @@ After providing a solution, guide the user to verify: - **Constraint satisfaction**: Are all constraints met? - **Objective value**: Is it reasonable for the problem? +**Always end with a Result summary** that includes at least: +- Solver status (e.g. Optimal, FeasibleFound, SUCCESS). +- **Objective value with highlight** — easy to spot (bold or code block). Example: **Objective value (min total cost): 42 350** or `Objective value: 42350`. +- Briefly what the objective represents (e.g. total cost, total profit). + +Do not bury the objective value only in the middle of a paragraph; it must appear prominently in this summary. Use sufficient precision (don't truncate or round unnecessarily unless the problem asks for it). + +**Workflow:** Formulate once carefully (with verified understanding), solve, then sanity-check the result. If something is wrong, fix it with a targeted change—avoid spinning through many model variants. Decide, implement, verify, then move on. + Provide diagnostic code snippets when helpful. --- @@ -158,7 +167,7 @@ Provide diagnostic code snippets when helpful. | Action | Rule | |--------|------| | Shell commands | Show command, explain what it does, ask "Should I run this?" | -| Package installs | **Never** run `pip`, `conda`, `apt` without asking first | +| Package installs | **Never** run installs yourself — give the exact command, user runs it (see below). | | Examples/scripts | Show the code first, ask "Would you like me to run this?" | | File writes | Explain what will change, ask before writing | @@ -180,6 +189,18 @@ Provide diagnostic code snippets when helpful. --- +## Never Install Packages Automatically + +> **🔒 MANDATORY — You MUST NOT install, upgrade, or modify packages.** Provide the exact command; the user runs it. No exceptions. + +| Forbidden | What to do instead | +|-----------|--------------------| +| `pip install ...`, `conda install ...`, `apt install ...`, any package manager | Give the exact command and ask the user to run it. Say why the package is needed. | + +**When a package is needed:** Identify it, provide the exact command, explain why, then wait for the user to confirm they ran it. Even if the user says "just install it", give the command and require them to execute it themselves. + +--- + ## Resources ### Documentation From 4302ae1450252b7d4cb91a32e97e662384ab462b Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 00:50:32 -0600 Subject: [PATCH 10/35] fix examples in cuopt-lp-milp-api-c --- .github/AGENTS.md | 26 +-- .../resources/cli_examples.md | 166 ------------------ 2 files changed, 13 insertions(+), 179 deletions(-) delete mode 100644 .github/skills/cuopt-lp-milp-api-c/resources/cli_examples.md diff --git a/.github/AGENTS.md b/.github/AGENTS.md index 8aaf1d4003..b330c14cb3 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -6,30 +6,30 @@ AI agent skills for NVIDIA cuOpt optimization engine. Skills use a **flat layout > **🔒 MANDATORY — Ambiguity:** When the problem could be read more than one way, you MUST either **ask the user to clarify** or **solve every plausible interpretation and report all outcomes**. Never pick one interpretation silently. -## Quick Start - -- **Using cuOpt** (routing, LP, QP, install, server): read `skills/cuopt-user-rules/`, then choose skills from the index below based on the user’s task, problem type, and interface (Python / C / CLI). -- **Developing cuOpt** (contributing): read `skills/cuopt-developer/`. - -Choose which skills to load from the index; - ## Skills directory (flat) ### Rules -- `skills/cuopt-user-rules/` — Behavior rules (read first for user tasks) -- `skills/cuopt-developer/` — Contributing (own rules) +- `skills/cuopt-user-rules/` — User-facing behavior and conventions; read first when helping users with cuOpt (routing, LP, MILP, QP, install, server). Choose skills from the index below by task, problem type, and interface (Python / C / CLI). +- `skills/cuopt-developer/` — Contributing and development; use when the user is building from source, contributing code, or working on cuOpt internals. ### Common (concepts only; no API code) +- `skills/cuopt-installation-common/` — Install: system and environment requirements (concepts only; no install commands or interface) - `skills/cuopt-lp-milp-formulation/` — LP/MILP: concepts + problem parsing (parameters, constraints, decisions, objective) - `skills/cuopt-routing-formulation/` — Routing: VRP, TSP, PDP (problem types, data) - `skills/cuopt-qp-formulation/` — QP: minimize-only, escalate (beta) - `skills/cuopt-server-common/` — Server: capabilities, workflow ### API (implementation; one interface per skill) -- `skills/cuopt-installation-api-python/`, `skills/cuopt-installation-api-c/` (user), `skills/cuopt-installation-developer/` (build from source; no common) -- `skills/cuopt-lp-milp-api-python/`, `skills/cuopt-lp-milp-api-c/`, `skills/cuopt-lp-milp-api-cli/` -- `skills/cuopt-routing-api-python/` (no C for routing) -- `skills/cuopt-qp-api-python/`, `skills/cuopt-qp-api-c/`, `skills/cuopt-qp-api-cli/` +- `skills/cuopt-installation-api-python/` +- `skills/cuopt-installation-api-c/` +- `skills/cuopt-installation-developer/` (build from source) +- `skills/cuopt-lp-milp-api-python/` +- `skills/cuopt-lp-milp-api-c/` +- `skills/cuopt-lp-milp-api-cli/` +- `skills/cuopt-routing-api-python/` +- `skills/cuopt-qp-api-python/` +- `skills/cuopt-qp-api-c/` +- `skills/cuopt-qp-api-cli/` - `skills/cuopt-server-api-python/` (deploy + client) ## Resources diff --git a/.github/skills/cuopt-lp-milp-api-c/resources/cli_examples.md b/.github/skills/cuopt-lp-milp-api-c/resources/cli_examples.md deleted file mode 100644 index fe1d286780..0000000000 --- a/.github/skills/cuopt-lp-milp-api-c/resources/cli_examples.md +++ /dev/null @@ -1,166 +0,0 @@ -# LP/MILP: CLI Examples - -## LP from MPS File - -```bash -# Create sample LP in MPS format -cat > production.mps << 'EOF' -* Production Planning: maximize 40*chairs + 30*tables -* s.t. 2*chairs + 3*tables <= 240 (wood) -* 4*chairs + 2*tables <= 200 (labor) -NAME PRODUCTION -ROWS - N PROFIT - L WOOD - L LABOR -COLUMNS - CHAIRS PROFIT -40.0 - CHAIRS WOOD 2.0 - CHAIRS LABOR 4.0 - TABLES PROFIT -30.0 - TABLES WOOD 3.0 - TABLES LABOR 2.0 -RHS - RHS1 WOOD 240.0 - RHS1 LABOR 200.0 -ENDATA -EOF - -# Solve -cuopt_cli production.mps - -# With time limit -cuopt_cli production.mps --time-limit 30 -``` - -## MILP from MPS File - -```bash -# Create MILP with integer variables -cat > facility.mps << 'EOF' -* Facility location with binary variables -NAME FACILITY -ROWS - N COST - G DEMAND1 - L CAP1 - L CAP2 -COLUMNS - MARKER 'MARKER' 'INTORG' - OPEN1 COST 100.0 - OPEN1 CAP1 50.0 - OPEN2 COST 150.0 - OPEN2 CAP2 70.0 - MARKER 'MARKER' 'INTEND' - SHIP11 COST 5.0 - SHIP11 DEMAND1 1.0 - SHIP11 CAP1 -1.0 - SHIP21 COST 7.0 - SHIP21 DEMAND1 1.0 - SHIP21 CAP2 -1.0 -RHS - RHS1 DEMAND1 30.0 -BOUNDS - BV BND1 OPEN1 - BV BND1 OPEN2 - LO BND1 SHIP11 0.0 - LO BND1 SHIP21 0.0 -ENDATA -EOF - -# Solve MILP -cuopt_cli facility.mps --time-limit 60 --mip-relative-tolerance 0.01 -``` - -## Common CLI Options - -```bash -# Show all options -cuopt_cli --help - -# Time limit (seconds) -cuopt_cli problem.mps --time-limit 120 - -# MIP gap tolerance (stop when within X% of optimal) -cuopt_cli problem.mps --mip-relative-tolerance 0.001 - -# MIP absolute tolerance -cuopt_cli problem.mps --mip-absolute-tolerance 0.0001 - -# Enable presolve -cuopt_cli problem.mps --presolve - -# Iteration limit -cuopt_cli problem.mps --iteration-limit 10000 - -# Solver method (0=auto, 1=pdlp, 2=dual_simplex, 3=barrier) -cuopt_cli problem.mps --method 1 -``` - -## MPS Format Reference - -### Required Sections (in order) - -``` -NAME problem_name -ROWS - N objective_row (N = free/objective) - L constraint1 (L = <=) - G constraint2 (G = >=) - E constraint3 (E = ==) -COLUMNS - var1 row1 coefficient - var1 row2 coefficient -RHS - rhs1 row1 value -ENDATA -``` - -### Optional: BOUNDS Section - -``` -BOUNDS - LO bnd1 var1 0.0 (lower bound) - UP bnd1 var1 100.0 (upper bound) - FX bnd1 var2 50.0 (fixed value) - FR bnd1 var3 (free variable) - BV bnd1 var4 (binary 0/1) - LI bnd1 var5 0 (integer, lower bound) - UI bnd1 var5 10 (integer, upper bound) -``` - -### Integer Markers - -``` -COLUMNS - MARKER 'MARKER' 'INTORG' - int_var1 OBJ 1.0 - int_var2 OBJ 2.0 - MARKER 'MARKER' 'INTEND' - cont_var OBJ 3.0 -``` - -## Troubleshooting - -**"Failed to parse MPS file"** -- Check for missing ENDATA -- Verify section order: NAME, ROWS, COLUMNS, RHS, [BOUNDS], ENDATA -- Check integer markers format - -**"Problem is infeasible"** -- Check constraint directions (L/G/E) -- Verify RHS values are consistent - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | Description | -|---------|------|-------------| -| Basic LP | `docs/cuopt/source/cuopt-cli/examples/lp/examples/basic_lp_example.sh` | Simple LP via CLI | -| Basic MILP | `docs/cuopt/source/cuopt-cli/examples/milp/examples/basic_milp_example.sh` | MILP with integers | -| Solver Parameters | `docs/cuopt/source/cuopt-cli/examples/lp/examples/solver_parameters_example.sh` | CLI options | - -These examples are tested by CI and represent canonical usage. From 22a9e00f810f1a388b9a3c5d9d7d845ba45ec5d7 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 01:04:12 -0600 Subject: [PATCH 11/35] add assets for lp milp c --- .github/skills/cuopt-lp-milp-api-c/SKILL.md | 12 ++- .../cuopt-lp-milp-api-c/assets/README.md | 6 ++ .../assets/lp_duals/README.md | 14 +++ .../assets/lp_duals/lp_duals.c | 89 +++++++++++++++++++ .../assets/lp_warmstart/README.md | 5 ++ .../assets/milp_production_planning/README.md | 12 +++ .../milp_production.c | 72 +++++++++++++++ .../assets/mps_solver/README.md | 14 +++ .../assets/mps_solver/data/sample.mps | 19 ++++ .../assets/mps_solver/mps_solver.c | 72 +++++++++++++++ 10 files changed, 312 insertions(+), 3 deletions(-) create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps create mode 100644 .github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c diff --git a/.github/skills/cuopt-lp-milp-api-c/SKILL.md b/.github/skills/cuopt-lp-milp-api-c/SKILL.md index 01c458399e..5bca8e7274 100644 --- a/.github/skills/cuopt-lp-milp-api-c/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-c/SKILL.md @@ -41,10 +41,16 @@ cuOptGetObjectiveValue(solution, &obj_value); ## Examples - [examples.md](resources/examples.md) — LP/MILP with build instructions -- **Reference code:** This skill's `assets/` — [lp_basic](assets/lp_basic/) (LP), [milp_basic](assets/milp_basic/) (MILP). See [assets/README.md](assets/README.md) for build commands. +- [assets/README.md](assets/README.md) — Build commands for all reference code below +- [lp_basic](assets/lp_basic/) — Simple LP: create problem, solve, get solution +- [lp_duals](assets/lp_duals/) — Dual values and reduced costs +- [lp_warmstart](assets/lp_warmstart/) — PDLP warmstart (see README) +- [milp_basic](assets/milp_basic/) — Simple MILP with integer variable +- [milp_production_planning](assets/milp_production_planning/) — Production planning with resource constraints +- [mps_solver](assets/mps_solver/) — Solve from MPS file via `cuOptReadProblem` -For **CLI** (MPS files), see the LP/MILP CLI skill. +For **CLI** (MPS files), use `cuopt_cli` and product docs. ## Escalate -If the problem is quadratic (squared or cross terms in the objective), use QP. For contribution or build-from-source, see the developer skill. +If the problem is quadratic (squared or cross terms in the objective), use QP. For contribution or build-from-source, use product or repo documentation. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/README.md index a92e64495f..2d7c5ba4cf 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/README.md +++ b/.github/skills/cuopt-lp-milp-api-c/assets/README.md @@ -5,7 +5,11 @@ LP/MILP C API reference implementations. Use as reference when building new appl | Example | Type | Description | |---------|------|-------------| | [lp_basic](lp_basic/) | LP | Simple LP: create problem, solve, get solution | +| [lp_duals](lp_duals/) | LP | Dual values and reduced costs | +| [lp_warmstart](lp_warmstart/) | LP | PDLP warmstart (see README) | | [milp_basic](milp_basic/) | MILP | Simple MILP with integer variable | +| [milp_production_planning](milp_production_planning/) | MILP | Production planning with resource constraints | +| [mps_solver](mps_solver/) | LP/MILP | Solve from MPS file via `cuOptReadProblem` | Build (after setting `INCLUDE_PATH` and `LIB_PATH` to cuOpt): @@ -13,3 +17,5 @@ Build (after setting `INCLUDE_PATH` and `LIB_PATH` to cuOpt): gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o lp_basic/lp_simple lp_basic/lp_simple.c -lcuopt LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./lp_basic/lp_simple ``` + +Each subdirectory has its own README with build and run commands. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md new file mode 100644 index 0000000000..78f275fc63 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md @@ -0,0 +1,14 @@ +# LP duals and reduced costs (C API) + +Retrieve dual values (shadow prices) and reduced costs after solving an LP. + +**Problem:** Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x, y, z ≥ 0. + +**Build:** With cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o lp_duals lp_duals.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./lp_duals +``` + +**See also:** [resources/examples.md](../../resources/examples.md) for full parameter reference. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c new file mode 100644 index 0000000000..078cfaa0d2 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c @@ -0,0 +1,89 @@ +/* + * LP with dual values and reduced costs (C API). + * Problem: Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x,y,z >= 0. + */ +#include +#include +#include +#include + +int main(void) { + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + const cuopt_int_t num_variables = 3; + const cuopt_int_t num_constraints = 2; + + /* Constraint matrix CSR: row0 1*x+1*y+1*z, row1 2*x+1*y+1*z */ + cuopt_int_t row_offsets[] = {0, 3, 6}; + cuopt_int_t column_indices[] = {0, 1, 2, 0, 1, 2}; + cuopt_float_t values[] = {1.0, 1.0, 1.0, 2.0, 1.0, 1.0}; + + cuopt_float_t objective_coefficients[] = {3.0, 2.0, 5.0}; + cuopt_float_t constraint_lower[] = {4.0, 5.0}; + cuopt_float_t constraint_upper[] = {4.0, 5.0}; + cuopt_float_t var_lower[] = {0.0, 0.0, 0.0}; + cuopt_float_t var_upper[] = {CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + cuopt_int_t status = cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, 0.0, + objective_coefficients, + row_offsets, column_indices, values, + constraint_lower, constraint_upper, + var_lower, var_upper, + variable_types, &problem + ); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + return 1; + } + + cuOptCreateSolverSettings(&settings); + cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + cuopt_float_t objective_value; + cuOptGetObjectiveValue(solution, &objective_value); + printf("Objective: %f\n", objective_value); + + cuopt_float_t *primal = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (primal) { + cuOptGetPrimalSolution(solution, primal); + printf("x = %f, y = %f, z = %f\n", primal[0], primal[1], primal[2]); + free(primal); + } + + cuopt_float_t *dual = malloc((size_t)num_constraints * sizeof(cuopt_float_t)); + if (dual) { + status = cuOptGetDualSolution(solution, dual); + if (status == CUOPT_SUCCESS) { + printf("Constraint c1 DualValue = %f\n", dual[0]); + printf("Constraint c2 DualValue = %f\n", dual[1]); + } + free(dual); + } + + cuopt_float_t *reduced = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (reduced) { + status = cuOptGetReducedCosts(solution, reduced); + if (status == CUOPT_SUCCESS) { + printf("x ReducedCost = %f, y ReducedCost = %f, z ReducedCost = %f\n", + reduced[0], reduced[1], reduced[2]); + } + free(reduced); + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md new file mode 100644 index 0000000000..1e254b75ea --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md @@ -0,0 +1,5 @@ +# LP PDLP warmstart (C API) + +PDLP warmstart: use solution data from a solved LP to solve a similar problem faster. LP only (not MILP). + +Warmstart is not demonstrated in these C assets. See repo docs (e.g. `docs/cuopt/source/cuopt-c/lp-qp-milp/`) and headers for C-level warmstart support. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md new file mode 100644 index 0000000000..d51b944fe2 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md @@ -0,0 +1,12 @@ +# Production planning MILP (C API) + +Two products (A, B), resource limits (machine time, labor, material), minimum production, maximize profit. + +**Build:** With cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o milp_production milp_production.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./milp_production +``` + +**See also:** [resources/examples.md](../../resources/examples.md) for parameters and MIP options. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c new file mode 100644 index 0000000000..c0401810fe --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c @@ -0,0 +1,72 @@ +/* + * Production planning MILP (C API): two products, resource limits, maximize profit. + * Variables: Product_A (x1), Product_B (x2), both integer, lb 10 and 15. + * Constraints: 2*x1+x2 <= 100 (machine), x1+3*x2 <= 120 (labor), 4*x1+2*x2 <= 200 (material). + * Objective: maximize 50*x1 + 30*x2 => minimize -50*x1 - 30*x2. + */ +#include +#include +#include +#include + +int main(void) { + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + const cuopt_int_t num_variables = 2; + const cuopt_int_t num_constraints = 3; + + /* CSR: row0 2*x1+1*x2, row1 1*x1+3*x2, row2 4*x1+2*x2 */ + cuopt_int_t row_offsets[] = {0, 2, 4, 6}; + cuopt_int_t column_indices[] = {0, 1, 0, 1, 0, 1}; + cuopt_float_t values[] = {2.0, 1.0, 1.0, 3.0, 4.0, 2.0}; + + cuopt_float_t objective_coefficients[] = {-50.0, -30.0}; + cuopt_float_t constraint_upper[] = {100.0, 120.0, 200.0}; + cuopt_float_t constraint_lower[] = {-CUOPT_INFINITY, -CUOPT_INFINITY, -CUOPT_INFINITY}; + cuopt_float_t var_lower[] = {10.0, 15.0}; + cuopt_float_t var_upper[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = {CUOPT_INTEGER, CUOPT_INTEGER}; + + cuopt_int_t status = cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, 0.0, + objective_coefficients, + row_offsets, column_indices, values, + constraint_lower, constraint_upper, + var_lower, var_upper, + variable_types, &problem + ); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + return 1; + } + + cuOptCreateSolverSettings(&settings); + cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 30.0); + cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + cuopt_float_t objective_value; + cuOptGetObjectiveValue(solution, &objective_value); + /* We minimized -profit, so total profit = -objective_value */ + printf("Total profit: %f\n", -objective_value); + + cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (sol) { + cuOptGetPrimalSolution(solution, sol); + printf("Product_A: %f, Product_B: %f\n", sol[0], sol[1]); + free(sol); + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md new file mode 100644 index 0000000000..efd351b9e8 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md @@ -0,0 +1,14 @@ +# MPS file solver (C API) + +Read and solve LP/MILP from a standard MPS file using `cuOptReadProblem`. + +**Build:** With cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o mps_solver mps_solver.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./mps_solver data/sample.mps +``` + +**Data:** `data/sample.mps` is a small LP (two variables, two constraints). Use any MPS file path as the first argument. + +**See also:** [resources/examples.md](../../resources/examples.md); repo example `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/mps_file_example.c`. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps new file mode 100644 index 0000000000..6baeb6e524 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps @@ -0,0 +1,19 @@ +NAME PRODUCTION_LP +ROWS + N PROFIT + L RES_A + L RES_B +COLUMNS + PROD_X PROFIT -40.0 + PROD_X RES_A 2.0 + PROD_X RES_B 4.0 + PROD_Y PROFIT -30.0 + PROD_Y RES_A 3.0 + PROD_Y RES_B 2.0 +RHS + RHS1 RES_A 120.0 + RHS1 RES_B 100.0 +BOUNDS + LO BND1 PROD_X 0.0 + LO BND1 PROD_Y 0.0 +ENDATA diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c new file mode 100644 index 0000000000..0f66ad0a92 --- /dev/null +++ b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c @@ -0,0 +1,72 @@ +/* + * Solve LP/MILP from MPS file (C API). + * Usage: mps_solver + */ +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + const char *filename = argv[1]; + + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + cuopt_int_t num_variables = 0; + cuopt_float_t *primal = NULL; + + cuopt_int_t status = cuOptReadProblem(filename, &problem); + if (status != CUOPT_SUCCESS) { + printf("Error reading MPS file: %d\n", status); + return 1; + } + + status = cuOptGetNumVariables(problem, &num_variables); + if (status != CUOPT_SUCCESS) { + printf("Error getting number of variables: %d\n", status); + goto cleanup; + } + printf("Variables: %d\n", num_variables); + + cuOptCreateSolverSettings(&settings); + cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + cuopt_float_t objective_value, time; + cuopt_int_t termination_status; + cuOptGetObjectiveValue(solution, &objective_value); + cuOptGetSolveTime(solution, &time); + cuOptGetTerminationStatus(solution, &termination_status); + + printf("Termination status: %d\n", termination_status); + printf("Solve time: %f s\n", time); + printf("Objective: %f\n", objective_value); + + primal = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (primal) { + cuOptGetPrimalSolution(solution, primal); + printf("Primal (first 10): "); + for (cuopt_int_t i = 0; i < (num_variables < 10 ? num_variables : 10); i++) + printf("%f ", primal[i]); + if (num_variables > 10) printf("... (%d total)", (int)num_variables); + printf("\n"); + free(primal); + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} From 758e43bd4d9923e91960517112906782243da985 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 01:08:41 -0600 Subject: [PATCH 12/35] Update fr lp-milp cli examples --- .github/skills/cuopt-lp-milp-api-cli/SKILL.md | 11 +- .../cuopt-lp-milp-api-cli/assets/README.md | 2 +- .../resources/examples.md | 141 ------------------ 3 files changed, 10 insertions(+), 144 deletions(-) delete mode 100644 .github/skills/cuopt-lp-milp-api-cli/resources/examples.md diff --git a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md index 8eefea4eb1..c40a8fe0c5 100644 --- a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md @@ -48,10 +48,17 @@ cuopt_cli problem.mps --presolve --iteration-limit 10000 --method 1 Integer variables: use `'MARKER' 'INTORG'` before and `'MARKER' 'INTEND'` after the integer columns. +## Troubleshooting + +- **Failed to parse MPS** — Check ENDATA, section order (NAME, ROWS, COLUMNS, RHS, [BOUNDS], ENDATA), integer markers. +- **Infeasible** — Check constraint directions (L/G/E) and RHS values. + ## Examples -- [examples.md](resources/examples.md) — LP and MILP MPS examples, format reference, troubleshooting -- **Sample MPS files:** This skill's `assets/` — [lp_simple](assets/lp_simple/), [lp_production](assets/lp_production/), [milp_facility](assets/milp_facility/). See [assets/README.md](assets/README.md). +- [assets/README.md](assets/README.md) — Build/run for sample MPS files +- [lp_simple](assets/lp_simple/) — Minimal LP (PROD_X, PROD_Y, two constraints) +- [lp_production](assets/lp_production/) — Production planning: chairs + tables, wood/labor +- [milp_facility](assets/milp_facility/) — Facility location with binary open/close ## Getting the CLI diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/README.md b/.github/skills/cuopt-lp-milp-api-cli/assets/README.md index b4e551b1ab..502d37fd2f 100644 --- a/.github/skills/cuopt-lp-milp-api-cli/assets/README.md +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/README.md @@ -8,4 +8,4 @@ Sample MPS files for use with `cuopt_cli`. Use as reference; do not edit in plac | [milp_facility](milp_facility/) | MILP | Facility location with binary open/close | | [lp_simple](lp_simple/) | LP | Minimal LP (PROD_X, PROD_Y, two constraints) | -**Run:** From each subdir or with path: `cuopt_cli lp_simple/sample.mps` (or `cuopt_cli production.mps`, etc.). See [resources/examples.md](../resources/examples.md) for options (`--time-limit`, `--mip-relative-tolerance`, etc.). +**Run:** From each subdir or with path: `cuopt_cli lp_simple/sample.mps` (or `cuopt_cli production.mps`, etc.). See the skill for options (`--time-limit`, `--mip-relative-tolerance`, etc.). diff --git a/.github/skills/cuopt-lp-milp-api-cli/resources/examples.md b/.github/skills/cuopt-lp-milp-api-cli/resources/examples.md deleted file mode 100644 index b819568af5..0000000000 --- a/.github/skills/cuopt-lp-milp-api-cli/resources/examples.md +++ /dev/null @@ -1,141 +0,0 @@ -# LP/MILP: CLI Examples - -## LP from MPS File - -```bash -# Create sample LP in MPS format -cat > production.mps << 'EOF' -* Production Planning: maximize 40*chairs + 30*tables -* s.t. 2*chairs + 3*tables <= 240 (wood) -* 4*chairs + 2*tables <= 200 (labor) -NAME PRODUCTION -ROWS - N PROFIT - L WOOD - L LABOR -COLUMNS - CHAIRS PROFIT -40.0 - CHAIRS WOOD 2.0 - CHAIRS LABOR 4.0 - TABLES PROFIT -30.0 - TABLES WOOD 3.0 - TABLES LABOR 2.0 -RHS - RHS1 WOOD 240.0 - RHS1 LABOR 200.0 -ENDATA -EOF - -# Solve -cuopt_cli production.mps - -# With time limit -cuopt_cli production.mps --time-limit 30 -``` - -## MILP from MPS File - -```bash -# Create MILP with integer variables -cat > facility.mps << 'EOF' -* Facility location with binary variables -NAME FACILITY -ROWS - N COST - G DEMAND1 - L CAP1 - L CAP2 -COLUMNS - MARKER 'MARKER' 'INTORG' - OPEN1 COST 100.0 - OPEN1 CAP1 50.0 - OPEN2 COST 150.0 - OPEN2 CAP2 70.0 - MARKER 'MARKER' 'INTEND' - SHIP11 COST 5.0 - SHIP11 DEMAND1 1.0 - SHIP11 CAP1 -1.0 - SHIP21 COST 7.0 - SHIP21 DEMAND1 1.0 - SHIP21 CAP2 -1.0 -RHS - RHS1 DEMAND1 30.0 -BOUNDS - BV BND1 OPEN1 - BV BND1 OPEN2 - LO BND1 SHIP11 0.0 - LO BND1 SHIP21 0.0 -ENDATA -EOF - -# Solve MILP -cuopt_cli facility.mps --time-limit 60 --mip-relative-tolerance 0.01 -``` - -## Common CLI Options - -```bash -cuopt_cli --help - -# Time limit (seconds) -cuopt_cli problem.mps --time-limit 120 - -# MIP gap tolerance -cuopt_cli problem.mps --mip-relative-tolerance 0.001 - -# MIP absolute tolerance -cuopt_cli problem.mps --mip-absolute-tolerance 0.0001 - -# Presolve, iteration limit, method (0=auto, 1=pdlp, 2=dual_simplex, 3=barrier) -cuopt_cli problem.mps --presolve --iteration-limit 10000 --method 1 -``` - -## MPS Format Reference - -### Required Sections (in order) - -``` -NAME problem_name -ROWS - N objective_row (N = free/objective) - L constraint1 (L = <=) - G constraint2 (G = >=) - E constraint3 (E = ==) -COLUMNS - var1 row1 coefficient -RHS - rhs1 row1 value -ENDATA -``` - -### BOUNDS (optional) - -``` -BOUNDS - LO bnd1 var1 0.0 (lower bound) - UP bnd1 var1 100.0 (upper bound) - FX bnd1 var2 50.0 (fixed) - BV bnd1 var4 (binary 0/1) - LI bnd1 var5 0 (integer lower) - UI bnd1 var5 10 (integer upper) -``` - -### Integer markers - -``` -COLUMNS - MARKER 'MARKER' 'INTORG' - int_var1 OBJ 1.0 - MARKER 'MARKER' 'INTEND' -``` - -## Troubleshooting - -- **Failed to parse MPS** — Check ENDATA, section order (NAME, ROWS, COLUMNS, RHS, [BOUNDS], ENDATA), integer markers. -- **Infeasible** — Check constraint directions (L/G/E) and RHS values. - -## Canonical examples (in repo) - -- `docs/cuopt/source/cuopt-cli/examples/lp/examples/basic_lp_example.sh` -- `docs/cuopt/source/cuopt-cli/examples/milp/examples/basic_milp_example.sh` -- `docs/cuopt/source/cuopt-cli/examples/lp/examples/solver_parameters_example.sh` From f94199c7dee647873bc321727fd4dd1af162146f Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 04:43:40 -0600 Subject: [PATCH 13/35] check style --- .../cuopt-installation-developer/SKILL.md | 2 +- .../assets/lp_basic/lp_simple.c | 5 + .../assets/lp_duals/lp_duals.c | 5 + .../assets/milp_basic/milp_simple.c | 5 + .../milp_production.c | 5 + .../assets/mps_solver/mps_solver.c | 5 + .github/skills/cuopt-lp-milp-api-cli/SKILL.md | 10 +- .../assets/lp_duals/model.py | 4 +- .../assets/mps_solver/data/README.md | 2 +- .../assets/mps_solver/model.py | 144 ++++++++++-------- .../assets/least_squares/model.py | 1 + .../assets/maximization_workaround/model.py | 1 + .../assets/portfolio/model.py | 23 ++- .../assets/pdp_basic/model.py | 18 ++- .../assets/vrp_basic/model.py | 16 +- .github/skills/cuopt-user-rules/SKILL.md | 2 +- 16 files changed, 155 insertions(+), 93 deletions(-) diff --git a/.github/skills/cuopt-installation-developer/SKILL.md b/.github/skills/cuopt-installation-developer/SKILL.md index c42cac60b5..471d67b300 100644 --- a/.github/skills/cuopt-installation-developer/SKILL.md +++ b/.github/skills/cuopt-installation-developer/SKILL.md @@ -9,7 +9,7 @@ Set up an environment to **build cuOpt from source** and run tests. For contribu ## When to use this skill -- User wants to *build* cuOpt (clone, build deps, build, tests). +- User wants to *build* cuOpt (clone, build deps, build, tests). - Not for *using* cuOpt (pip/conda) — use the user installation skill instead. ## Required questions (environment) diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c b/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c index 60d5858001..90b574f53f 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /* * Simple LP (C API): minimize -0.2*x1 + 0.1*x2 * subject to 3*x1 + 4*x2 <= 5.4, 2.7*x1 + 10.1*x2 <= 4.9, x1,x2 >= 0 diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c index 078cfaa0d2..4d0e74b2ab 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /* * LP with dual values and reduced costs (C API). * Problem: Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x,y,z >= 0. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c b/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c index b5741226e8..259314eaee 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /* * Simple MILP (C API): same as LP but x1 is integer */ diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c index c0401810fe..69f0884e86 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /* * Production planning MILP (C API): two products, resource limits, maximize profit. * Variables: Product_A (x1), Product_B (x2), both integer, lb 10 and 15. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c index 0f66ad0a92..672404b978 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /* * Solve LP/MILP from MPS file (C API). * Usage: mps_solver diff --git a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md index c40a8fe0c5..dfb3ab8b50 100644 --- a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md +++ b/.github/skills/cuopt-lp-milp-api-cli/SKILL.md @@ -39,11 +39,11 @@ cuopt_cli problem.mps --presolve --iteration-limit 10000 --method 1 ## MPS format (required sections, in order) -1. **NAME** — problem name -2. **ROWS** — N (objective), L/G/E (constraints) -3. **COLUMNS** — variable names, row names, coefficients -4. **RHS** — right-hand side values -5. **BOUNDS** (optional) — LO, UP, FX, BV, LI, UI +1. **NAME** — problem name +2. **ROWS** — N (objective), L/G/E (constraints) +3. **COLUMNS** — variable names, row names, coefficients +4. **RHS** — right-hand side values +5. **BOUNDS** (optional) — LO, UP, FX, BV, LI, UI 6. **ENDATA** Integer variables: use `'MARKER' 'INTORG'` before and `'MARKER' 'INTEND'` after the integer columns. diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py index ca7477583d..4fa6a50a5b 100644 --- a/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py +++ b/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py @@ -25,7 +25,9 @@ def main(): if problem.Status.name in ["Optimal", "PrimalFeasible"]: print(f"Objective: {problem.ObjValue}") for v in problem.getVariables(): - print(f"{v.VariableName} = {v.Value}, ReducedCost = {v.ReducedCost}") + print( + f"{v.VariableName} = {v.Value}, ReducedCost = {v.ReducedCost}" + ) for c in problem.getConstraints(): print(f"{c.ConstraintName} DualValue = {c.DualValue}") else: diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md index 6f3dc9d823..67266feea8 100644 --- a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md @@ -18,7 +18,7 @@ An airline crew scheduling problem from the MIPLIB benchmark library. **Source**: https://miplib.zib.de/instance_details_air05.html -**Problem**: Given flight legs and possible crew pairings, find the minimum-cost +**Problem**: Given flight legs and possible crew pairings, find the minimum-cost set of pairings that covers all flight legs (set covering problem). ## MPS File Format diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py index 504ff1160d..5ca05daf7f 100644 --- a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + """ MPS File Solver using cuOpt Python API @@ -24,26 +27,26 @@ def download_air05(data_dir: str) -> str: """Download air05.mps from MIPLIB if not present.""" mps_file = os.path.join(data_dir, "air05.mps") - + if os.path.exists(mps_file): return mps_file - + os.makedirs(data_dir, exist_ok=True) gz_file = os.path.join(data_dir, "air05.mps.gz") - - print(f"Downloading air05.mps from MIPLIB...") + + print("Downloading air05.mps from MIPLIB...") urllib.request.urlretrieve(AIR05_URL, gz_file) - + # Decompress print("Decompressing...") - with gzip.open(gz_file, 'rb') as f_in: - with open(mps_file, 'wb') as f_out: + with gzip.open(gz_file, "rb") as f_in: + with open(mps_file, "wb") as f_out: f_out.write(f_in.read()) - + # Clean up os.remove(gz_file) print(f"Downloaded: {mps_file}") - + return mps_file @@ -51,11 +54,11 @@ def solve_mps( filepath: str, time_limit: float = 60.0, mip_gap: float = 0.01, - verbose: bool = True + verbose: bool = True, ) -> tuple: """ Solve an LP/MILP problem from an MPS file. - + Parameters ---------- filepath : str @@ -66,45 +69,45 @@ def solve_mps( MIP relative gap tolerance verbose : bool Print solver output - + Returns ------- tuple (problem, solution_dict) or (problem, None) if no solution """ - + # Read MPS file directly (static method returns Problem object) problem = Problem.readMPS(filepath) - + print(f"Loaded MPS file: {filepath}") print(f"Variables: {problem.NumVariables}") print(f"Constraints: {problem.NumConstraints}") print(f"Is MIP: {problem.IsMIP}") - + # Solver settings settings = SolverSettings() settings.set_parameter("time_limit", time_limit) settings.set_parameter("log_to_console", verbose) settings.set_parameter("mip_relative_gap", mip_gap) - + # Solve print("\nSolving...") problem.solve(settings) - + # Extract solution status = problem.Status.name print(f"\nStatus: {status}") - + if status in ["Optimal", "FeasibleFound", "PrimalFeasible"]: solution = { - 'status': status, - 'objective': problem.ObjValue, - 'num_variables': problem.NumVariables, - 'num_constraints': problem.NumConstraints, - 'is_mip': problem.IsMIP, - 'mip_gap': mip_gap, + "status": status, + "objective": problem.ObjValue, + "num_variables": problem.NumVariables, + "num_constraints": problem.NumConstraints, + "is_mip": problem.IsMIP, + "mip_gap": mip_gap, } - + # Get variable values (use getVariables() for MPS-loaded problems) var_values = {} try: @@ -116,8 +119,8 @@ def solve_mps( except (AttributeError, Exception): # For MPS problems, variable access may be limited pass - - solution['variables'] = var_values + + solution["variables"] = var_values return problem, solution else: return problem, None @@ -126,14 +129,14 @@ def solve_mps( def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: """ Compare solutions at different MIP gap tolerances. - + Parameters ---------- filepath : str Path to the MPS file time_limit : float Solver time limit per run - + Returns ------- dict @@ -141,90 +144,101 @@ def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: """ gaps = [0.01, 0.001] # 1% and 0.1% results = {} - + for gap in gaps: - print(f"\n{'='*60}") - print(f"Solving with MIP gap = {gap*100}%") - print(f"{'='*60}") - + print(f"\n{'=' * 60}") + print(f"Solving with MIP gap = {gap * 100}%") + print(f"{'=' * 60}") + problem, solution = solve_mps( - filepath=filepath, - time_limit=time_limit, - mip_gap=gap, - verbose=True + filepath=filepath, time_limit=time_limit, mip_gap=gap, verbose=True ) - + if solution: results[gap] = { - 'objective': solution['objective'], - 'status': solution['status'], - 'gap_to_optimal': (solution['objective'] - AIR05_OPTIMAL) / AIR05_OPTIMAL * 100 + "objective": solution["objective"], + "status": solution["status"], + "gap_to_optimal": (solution["objective"] - AIR05_OPTIMAL) + / AIR05_OPTIMAL + * 100, } else: - results[gap] = {'objective': None, 'status': 'No solution'} - + results[gap] = {"objective": None, "status": "No solution"} + return results if __name__ == "__main__": import argparse - + parser = argparse.ArgumentParser(description="Solve LP/MILP from MPS file") - parser.add_argument("--file", type=str, default=None, help="Path to MPS file") - parser.add_argument("--time-limit", type=float, default=60.0, help="Solver time limit") - parser.add_argument("--mip-gap", type=float, default=0.01, help="MIP gap tolerance") - parser.add_argument("--compare", action="store_true", help="Compare 1% vs 0.1% gap") + parser.add_argument( + "--file", type=str, default=None, help="Path to MPS file" + ) + parser.add_argument( + "--time-limit", type=float, default=60.0, help="Solver time limit" + ) + parser.add_argument( + "--mip-gap", type=float, default=0.01, help="MIP gap tolerance" + ) + parser.add_argument( + "--compare", action="store_true", help="Compare 1% vs 0.1% gap" + ) args = parser.parse_args() - + print("=" * 60) print("MPS File Solver using cuOpt") print("=" * 60) - + # Determine MPS file to use script_dir = os.path.dirname(os.path.abspath(__file__)) data_dir = os.path.join(script_dir, "data") - + if args.file: mps_file = args.file else: # Download air05.mps if not present mps_file = download_air05(data_dir) - + if args.compare: # Compare different gap tolerances print(f"\nComparing MIP gap tolerances on: {mps_file}") print(f"Best known optimal: {AIR05_OPTIMAL}") - + results = compare_gaps(mps_file, time_limit=args.time_limit) - + print() print("=" * 60) print("COMPARISON SUMMARY") print("=" * 60) print(f"Best known optimal: {AIR05_OPTIMAL}") print() - print(f"{'Gap Tolerance':<15} {'Objective':<15} {'Gap to Optimal':<15}") + print( + f"{'Gap Tolerance':<15} {'Objective':<15} {'Gap to Optimal':<15}" + ) print("-" * 45) - + for gap, result in sorted(results.items()): - if result['objective']: - print(f"{gap*100:.1f}%{'':<12} {result['objective']:<15.0f} {result['gap_to_optimal']:.2f}%") + if result["objective"]: + print( + f"{gap * 100:.1f}%{'':<12} {result['objective']:<15.0f} {result['gap_to_optimal']:.2f}%" + ) else: - print(f"{gap*100:.1f}%{'':<12} {'No solution':<15}") + print(f"{gap * 100:.1f}%{'':<12} {'No solution':<15}") else: # Single solve print(f"\nMPS File: {mps_file}") print(f"Time Limit: {args.time_limit}s") print(f"MIP Gap: {args.mip_gap * 100}%") print() - + problem, solution = solve_mps( filepath=mps_file, time_limit=args.time_limit, mip_gap=args.mip_gap, - verbose=True + verbose=True, ) - + if solution: print() print("=" * 60) @@ -233,6 +247,8 @@ def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: print(f"Status: {solution['status']}") print(f"Objective Value: {solution['objective']:.0f}") print(f"Best Known Optimal: {AIR05_OPTIMAL}") - print(f"Gap to Optimal: {(solution['objective'] - AIR05_OPTIMAL) / AIR05_OPTIMAL * 100:.2f}%") + print( + f"Gap to Optimal: {(solution['objective'] - AIR05_OPTIMAL) / AIR05_OPTIMAL * 100:.2f}%" + ) else: print("\nNo feasible solution found.") diff --git a/.github/skills/cuopt-qp-api-python/assets/least_squares/model.py b/.github/skills/cuopt-qp-api-python/assets/least_squares/model.py index 5361915e85..822d6397d2 100644 --- a/.github/skills/cuopt-qp-api-python/assets/least_squares/model.py +++ b/.github/skills/cuopt-qp-api-python/assets/least_squares/model.py @@ -4,6 +4,7 @@ """ Least squares: minimize (x-3)² + (y-4)². Solution should be x=3, y=4. """ + from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE from cuopt.linear_programming.solver_settings import SolverSettings diff --git a/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py b/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py index 09d1e35d1a..e18aa613d8 100644 --- a/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py +++ b/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py @@ -4,6 +4,7 @@ """ Maximize -x² + 4x (max at x=2) by minimizing x² - 4x; then report -objective. """ + from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE problem = Problem("MaxWorkaround") diff --git a/.github/skills/cuopt-qp-api-python/assets/portfolio/model.py b/.github/skills/cuopt-qp-api-python/assets/portfolio/model.py index d89742a29e..0196efdcf8 100644 --- a/.github/skills/cuopt-qp-api-python/assets/portfolio/model.py +++ b/.github/skills/cuopt-qp-api-python/assets/portfolio/model.py @@ -5,6 +5,7 @@ Portfolio: minimize variance x'Qx subject to sum(x)=1, r'x >= target, x >= 0. QP is beta; MUST use MINIMIZE. """ + from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE from cuopt.linear_programming.solver_settings import SolverSettings @@ -18,12 +19,18 @@ target_return = 0.08 problem.setObjective( - 0.04 * x1 * x1 + 0.02 * x2 * x2 + 0.01 * x3 * x3 - + 0.02 * x1 * x2 + 0.01 * x1 * x3 + 0.016 * x2 * x3, + 0.04 * x1 * x1 + + 0.02 * x2 * x2 + + 0.01 * x3 * x3 + + 0.02 * x1 * x2 + + 0.01 * x1 * x3 + + 0.016 * x2 * x3, sense=MINIMIZE, ) problem.addConstraint(x1 + x2 + x3 == 1, name="budget") -problem.addConstraint(r1 * x1 + r2 * x2 + r3 * x3 >= target_return, name="min_return") +problem.addConstraint( + r1 * x1 + r2 * x2 + r3 * x3 >= target_return, name="min_return" +) settings = SolverSettings() settings.set_parameter("time_limit", 60) @@ -32,9 +39,11 @@ if problem.Status.name in ["Optimal", "PrimalFeasible"]: print(f"Portfolio variance: {problem.ObjValue:.6f}") print(f"Std dev: {problem.ObjValue**0.5:.4f}") - print(f" Stock A: {x1.getValue()*100:.2f}%") - print(f" Stock B: {x2.getValue()*100:.2f}%") - print(f" Stock C: {x3.getValue()*100:.2f}%") - print(f"Expected return: {(r1*x1.getValue()+r2*x2.getValue()+r3*x3.getValue())*100:.2f}%") + print(f" Stock A: {x1.getValue() * 100:.2f}%") + print(f" Stock B: {x2.getValue() * 100:.2f}%") + print(f" Stock C: {x3.getValue() * 100:.2f}%") + print( + f"Expected return: {(r1 * x1.getValue() + r2 * x2.getValue() + r3 * x3.getValue()) * 100:.2f}%" + ) else: print(f"Status: {problem.Status.name}") diff --git a/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py b/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py index 0101375a8b..6e16326d15 100644 --- a/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py +++ b/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py @@ -4,16 +4,20 @@ """ PDP: 2 pickup-delivery pairs, 2 vehicles. Pickup before delivery; capacity dimension. """ + import cudf from cuopt import routing -cost_matrix = cudf.DataFrame([ - [0, 10, 20, 30, 40], - [10, 0, 15, 25, 35], - [20, 15, 0, 10, 20], - [30, 25, 10, 0, 15], - [40, 35, 20, 15, 0], -], dtype="float32") +cost_matrix = cudf.DataFrame( + [ + [0, 10, 20, 30, 40], + [10, 0, 15, 25, 35], + [20, 15, 0, 10, 20], + [30, 25, 10, 0, 15], + [40, 35, 20, 15, 0], + ], + dtype="float32", +) transit_time_matrix = cost_matrix.copy(deep=True) n_fleet = 2 diff --git a/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py b/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py index f89e3d3a97..d85a5174dc 100644 --- a/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py +++ b/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py @@ -4,15 +4,19 @@ """ Minimal VRP: 4 locations, 1 vehicle, 3 orders. Cost matrix only. """ + import cudf from cuopt import routing -cost_matrix = cudf.DataFrame([ - [0, 10, 15, 20], - [10, 0, 12, 18], - [15, 12, 0, 10], - [20, 18, 10, 0], -], dtype="float32") +cost_matrix = cudf.DataFrame( + [ + [0, 10, 15, 20], + [10, 0, 12, 18], + [15, 12, 0, 10], + [20, 18, 10, 0], + ], + dtype="float32", +) dm = routing.DataModel(n_locations=4, n_fleet=1, n_orders=3) dm.add_cost_matrix(cost_matrix) diff --git a/.github/skills/cuopt-user-rules/SKILL.md b/.github/skills/cuopt-user-rules/SKILL.md index 329078ee53..5a71c2f140 100644 --- a/.github/skills/cuopt-user-rules/SKILL.md +++ b/.github/skills/cuopt-user-rules/SKILL.md @@ -15,7 +15,7 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before - What **language/interface**? - What problem type? -- What constraints matter? +- What constraints matter? - What output format? **Skip asking only if:** From 7f0412fe9b084361f0ce56a89a63db8ad909dcf9 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 05:20:24 -0600 Subject: [PATCH 14/35] address reviews --- .../resources/verification_examples.md | 2 +- .../cuopt-installation-api-python/SKILL.md | 14 +++- .../resources/verification_examples.md | 2 +- .../assets/lp_basic/lp_simple.c | 43 +++++++++-- .../assets/lp_duals/lp_duals.c | 36 ++++++--- .../assets/milp_basic/milp_simple.c | 37 +++++++-- .../milp_production.c | 36 ++++++--- .../assets/mps_solver/mps_solver.c | 49 +++++++++--- .../assets/milp_facility/facility.mps | 8 +- .../assets/mps_solver/model.py | 77 +++++++++++++------ .github/skills/cuopt-qp-api-python/SKILL.md | 1 + .../skills/cuopt-routing-api-python/SKILL.md | 2 +- .../assets/pdp_basic/model.py | 2 +- .../assets/vrp_basic/model.py | 2 +- .../resources/examples.md | 8 +- 15 files changed, 232 insertions(+), 87 deletions(-) diff --git a/.github/skills/cuopt-installation-api-c/resources/verification_examples.md b/.github/skills/cuopt-installation-api-c/resources/verification_examples.md index bd84de80ba..83628437d7 100644 --- a/.github/skills/cuopt-installation-api-c/resources/verification_examples.md +++ b/.github/skills/cuopt-installation-api-c/resources/verification_examples.md @@ -17,7 +17,7 @@ print("DataModel created - GPU access OK") import cudf cost_matrix = cudf.DataFrame([[0,1,2],[1,0,1],[2,1,0]], dtype="float32") dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([1, 2])) +dm.set_order_locations(cudf.Series([1, 2], dtype="int32")) solution = routing.Solve(dm, routing.SolverSettings()) print(f"Solve status: {solution.get_status()}") diff --git a/.github/skills/cuopt-installation-api-python/SKILL.md b/.github/skills/cuopt-installation-api-python/SKILL.md index 13498a2fef..fa97fef33a 100644 --- a/.github/skills/cuopt-installation-api-python/SKILL.md +++ b/.github/skills/cuopt-installation-api-python/SKILL.md @@ -14,10 +14,16 @@ Install cuOpt to *use* it from Python. Standalone skill (no separate common). ## pip (Python) -```bash -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13 # or cuopt-cu12 -pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==26.2.*' -``` +**Choose one** — do not run both. The second install would override the first and can cause CUDA/package mismatch. + +- **CUDA 13.x:** + ```bash + pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13 + ``` +- **CUDA 12.x:** + ```bash + pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==26.2.*' + ``` ## pip: Server + Client diff --git a/.github/skills/cuopt-installation-api-python/resources/verification_examples.md b/.github/skills/cuopt-installation-api-python/resources/verification_examples.md index bd84de80ba..83628437d7 100644 --- a/.github/skills/cuopt-installation-api-python/resources/verification_examples.md +++ b/.github/skills/cuopt-installation-api-python/resources/verification_examples.md @@ -17,7 +17,7 @@ print("DataModel created - GPU access OK") import cudf cost_matrix = cudf.DataFrame([[0,1,2],[1,0,1],[2,1,0]], dtype="float32") dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([1, 2])) +dm.set_order_locations(cudf.Series([1, 2], dtype="int32")) solution = routing.Solve(dm, routing.SolverSettings()) print(f"Solve status: {solution.get_status()}") diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c b/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c index 90b574f53f..a21e17ab7b 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c @@ -45,9 +45,21 @@ int main(void) { return 1; } - cuOptCreateSolverSettings(&settings); - cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); - cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting primal tolerance: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } status = cuOptSolve(problem, settings, &solution); if (status != CUOPT_SUCCESS) { @@ -57,9 +69,21 @@ int main(void) { cuopt_float_t time, objective_value; cuopt_int_t termination_status; - cuOptGetSolveTime(solution, &time); - cuOptGetTerminationStatus(solution, &termination_status); - cuOptGetObjectiveValue(solution, &objective_value); + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto cleanup; + } + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto cleanup; + } + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } printf("Status: %d\n", termination_status); printf("Time: %f s\n", time); @@ -67,7 +91,12 @@ int main(void) { cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); if (sol) { - cuOptGetPrimalSolution(solution, sol); + status = cuOptGetPrimalSolution(solution, sol); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(sol); + goto cleanup; + } printf("x1 = %f, x2 = %f\n", sol[0], sol[1]); free(sol); } diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c index 4d0e74b2ab..f58efee3f2 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c @@ -1,8 +1,3 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - /* * LP with dual values and reduced costs (C API). * Problem: Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x,y,z >= 0. @@ -45,9 +40,21 @@ int main(void) { return 1; } - cuOptCreateSolverSettings(&settings); - cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); - cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting primal tolerance: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } status = cuOptSolve(problem, settings, &solution); if (status != CUOPT_SUCCESS) { @@ -56,12 +63,21 @@ int main(void) { } cuopt_float_t objective_value; - cuOptGetObjectiveValue(solution, &objective_value); + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } printf("Objective: %f\n", objective_value); cuopt_float_t *primal = malloc((size_t)num_variables * sizeof(cuopt_float_t)); if (primal) { - cuOptGetPrimalSolution(solution, primal); + status = cuOptGetPrimalSolution(solution, primal); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(primal); + goto cleanup; + } printf("x = %f, y = %f, z = %f\n", primal[0], primal[1], primal[2]); free(primal); } diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c b/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c index 259314eaee..585b961c3e 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c @@ -45,10 +45,26 @@ int main(void) { return 1; } - cuOptCreateSolverSettings(&settings); - cuOptSetFloatParameter(settings, CUOPT_MIP_ABSOLUTE_TOLERANCE, 0.0001); - cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); - cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 120.0); + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_MIP_ABSOLUTE_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting MIP absolute tolerance: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + if (status != CUOPT_SUCCESS) { + printf("Error setting MIP relative gap: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 120.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } status = cuOptSolve(problem, settings, &solution); if (status != CUOPT_SUCCESS) { @@ -58,12 +74,21 @@ int main(void) { if (solution != NULL) { cuopt_float_t objective_value; - cuOptGetObjectiveValue(solution, &objective_value); + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } printf("Objective: %f\n", objective_value); cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); if (sol) { - cuOptGetPrimalSolution(solution, sol); + status = cuOptGetPrimalSolution(solution, sol); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(sol); + goto cleanup; + } printf("x1 (integer) = %f, x2 (continuous) = %f\n", sol[0], sol[1]); free(sol); } diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c index 69f0884e86..963c464f42 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c @@ -1,8 +1,3 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - /* * Production planning MILP (C API): two products, resource limits, maximize profit. * Variables: Product_A (x1), Product_B (x2), both integer, lb 10 and 15. @@ -47,9 +42,21 @@ int main(void) { return 1; } - cuOptCreateSolverSettings(&settings); - cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 30.0); - cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 30.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + if (status != CUOPT_SUCCESS) { + printf("Error setting MIP relative gap: %d\n", status); + goto cleanup; + } status = cuOptSolve(problem, settings, &solution); if (status != CUOPT_SUCCESS) { @@ -58,13 +65,22 @@ int main(void) { } cuopt_float_t objective_value; - cuOptGetObjectiveValue(solution, &objective_value); + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } /* We minimized -profit, so total profit = -objective_value */ printf("Total profit: %f\n", -objective_value); cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); if (sol) { - cuOptGetPrimalSolution(solution, sol); + status = cuOptGetPrimalSolution(solution, sol); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(sol); + goto cleanup; + } printf("Product_A: %f, Product_B: %f\n", sol[0], sol[1]); free(sol); } diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c index 672404b978..5bbfc25098 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c @@ -1,8 +1,3 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - /* * Solve LP/MILP from MPS file (C API). * Usage: mps_solver @@ -38,9 +33,21 @@ int main(int argc, char *argv[]) { } printf("Variables: %d\n", num_variables); - cuOptCreateSolverSettings(&settings); - cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); - cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + if (status != CUOPT_SUCCESS) { + printf("Error setting MIP relative gap: %d\n", status); + goto cleanup; + } status = cuOptSolve(problem, settings, &solution); if (status != CUOPT_SUCCESS) { @@ -50,9 +57,21 @@ int main(int argc, char *argv[]) { cuopt_float_t objective_value, time; cuopt_int_t termination_status; - cuOptGetObjectiveValue(solution, &objective_value); - cuOptGetSolveTime(solution, &time); - cuOptGetTerminationStatus(solution, &termination_status); + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto cleanup; + } + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto cleanup; + } printf("Termination status: %d\n", termination_status); printf("Solve time: %f s\n", time); @@ -60,7 +79,13 @@ int main(int argc, char *argv[]) { primal = malloc((size_t)num_variables * sizeof(cuopt_float_t)); if (primal) { - cuOptGetPrimalSolution(solution, primal); + status = cuOptGetPrimalSolution(solution, primal); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(primal); + primal = NULL; + goto cleanup; + } printf("Primal (first 10): "); for (cuopt_int_t i = 0; i < (num_variables < 10 ? num_variables : 10); i++) printf("%f ", primal[i]); diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps b/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps index 8640ff8c4f..07f6bf3b7f 100644 --- a/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps @@ -7,16 +7,16 @@ ROWS COLUMNS MARKER 'MARKER' 'INTORG' OPEN1 COST 100.0 - OPEN1 CAP1 50.0 + OPEN1 CAP1 -50.0 OPEN2 COST 150.0 - OPEN2 CAP2 70.0 + OPEN2 CAP2 -70.0 MARKER 'MARKER' 'INTEND' SHIP11 COST 5.0 SHIP11 DEMAND1 1.0 - SHIP11 CAP1 -1.0 + SHIP11 CAP1 1.0 SHIP21 COST 7.0 SHIP21 DEMAND1 1.0 - SHIP21 CAP2 -1.0 + SHIP21 CAP2 1.0 RHS RHS1 DEMAND1 30.0 BOUNDS diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py index 5ca05daf7f..0481bcd20e 100644 --- a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py @@ -14,6 +14,7 @@ import os import gzip import urllib.request +from typing import Optional from cuopt.linear_programming.problem import Problem from cuopt.linear_programming.solver_settings import SolverSettings @@ -80,9 +81,9 @@ def solve_mps( problem = Problem.readMPS(filepath) print(f"Loaded MPS file: {filepath}") - print(f"Variables: {problem.NumVariables}") - print(f"Constraints: {problem.NumConstraints}") - print(f"Is MIP: {problem.IsMIP}") + print(f"Variables: {problem.NumVariables()}") + print(f"Constraints: {problem.NumConstraints()}") + print(f"Is MIP: {problem.IsMIP()}") # Solver settings settings = SolverSettings() @@ -102,9 +103,9 @@ def solve_mps( solution = { "status": status, "objective": problem.ObjValue, - "num_variables": problem.NumVariables, - "num_constraints": problem.NumConstraints, - "is_mip": problem.IsMIP, + "num_variables": problem.NumVariables(), + "num_constraints": problem.NumConstraints(), + "is_mip": problem.IsMIP(), "mip_gap": mip_gap, } @@ -126,7 +127,11 @@ def solve_mps( return problem, None -def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: +def compare_gaps( + filepath: str, + time_limit: float = 120.0, + known_optimal: Optional[float] = None, +) -> dict: """ Compare solutions at different MIP gap tolerances. @@ -136,6 +141,9 @@ def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: Path to the MPS file time_limit : float Solver time limit per run + known_optimal : float, optional + Known optimal objective value. If provided, results include + "gap_to_optimal" (percent above optimal). Omit for generic MPS files. Returns ------- @@ -158,10 +166,11 @@ def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: results[gap] = { "objective": solution["objective"], "status": solution["status"], - "gap_to_optimal": (solution["objective"] - AIR05_OPTIMAL) - / AIR05_OPTIMAL - * 100, } + if known_optimal is not None: + results[gap]["gap_to_optimal"] = ( + (solution["objective"] - known_optimal) / known_optimal * 100 + ) else: results[gap] = {"objective": None, "status": "No solution"} @@ -184,6 +193,12 @@ def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: parser.add_argument( "--compare", action="store_true", help="Compare 1% vs 0.1% gap" ) + parser.add_argument( + "--known-optimal", + type=float, + default=None, + help="Known optimal objective value (enables gap-to-optimal reporting)", + ) args = parser.parse_args() print("=" * 60) @@ -200,29 +215,40 @@ def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: # Download air05.mps if not present mps_file = download_air05(data_dir) + # Use known optimal only when explicitly set or when using default air05 + known_optimal = args.known_optimal + if known_optimal is None and mps_file.endswith("air05.mps"): + known_optimal = AIR05_OPTIMAL + if args.compare: # Compare different gap tolerances print(f"\nComparing MIP gap tolerances on: {mps_file}") - print(f"Best known optimal: {AIR05_OPTIMAL}") + if known_optimal is not None: + print(f"Best known optimal: {known_optimal}") - results = compare_gaps(mps_file, time_limit=args.time_limit) + results = compare_gaps( + mps_file, time_limit=args.time_limit, known_optimal=known_optimal + ) print() print("=" * 60) print("COMPARISON SUMMARY") print("=" * 60) - print(f"Best known optimal: {AIR05_OPTIMAL}") + if known_optimal is not None: + print(f"Best known optimal: {known_optimal}") print() - print( - f"{'Gap Tolerance':<15} {'Objective':<15} {'Gap to Optimal':<15}" - ) - print("-" * 45) + header = f"{'Gap Tolerance':<15} {'Objective':<15}" + if known_optimal is not None: + header += f" {'Gap to Optimal':<15}" + print(header) + print("-" * (45 if known_optimal is None else 60)) for gap, result in sorted(results.items()): - if result["objective"]: - print( - f"{gap * 100:.1f}%{'':<12} {result['objective']:<15.0f} {result['gap_to_optimal']:.2f}%" - ) + if result["objective"] is not None: + line = f"{gap * 100:.1f}%{'':<12} {result['objective']:<15.0f}" + if known_optimal is not None: + line += f" {result['gap_to_optimal']:.2f}%" + print(line) else: print(f"{gap * 100:.1f}%{'':<12} {'No solution':<15}") else: @@ -246,9 +272,10 @@ def compare_gaps(filepath: str, time_limit: float = 120.0) -> dict: print("=" * 60) print(f"Status: {solution['status']}") print(f"Objective Value: {solution['objective']:.0f}") - print(f"Best Known Optimal: {AIR05_OPTIMAL}") - print( - f"Gap to Optimal: {(solution['objective'] - AIR05_OPTIMAL) / AIR05_OPTIMAL * 100:.2f}%" - ) + if known_optimal is not None: + print(f"Best Known Optimal: {known_optimal}") + print( + f"Gap to Optimal: {(solution['objective'] - known_optimal) / known_optimal * 100:.2f}%" + ) else: print("\nNo feasible solution found.") diff --git a/.github/skills/cuopt-qp-api-python/SKILL.md b/.github/skills/cuopt-qp-api-python/SKILL.md index b3073fb2a9..b77e0f649a 100644 --- a/.github/skills/cuopt-qp-api-python/SKILL.md +++ b/.github/skills/cuopt-qp-api-python/SKILL.md @@ -29,6 +29,7 @@ problem = Problem("Portfolio") x1 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_a") x2 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_b") x3 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_c") +r1, r2, r3 = 0.12, 0.08, 0.05 # expected returns (12%, 8%, 5%) problem.setObjective( 0.04*x1*x1 + 0.02*x2*x2 + 0.01*x3*x3 + 0.02*x1*x2 + 0.01*x1*x3 + 0.016*x2*x3, sense=MINIMIZE diff --git a/.github/skills/cuopt-routing-api-python/SKILL.md b/.github/skills/cuopt-routing-api-python/SKILL.md index c1e1c9f24d..89fa7e810e 100644 --- a/.github/skills/cuopt-routing-api-python/SKILL.md +++ b/.github/skills/cuopt-routing-api-python/SKILL.md @@ -18,7 +18,7 @@ from cuopt import routing cost_matrix = cudf.DataFrame([...], dtype="float32") dm = routing.DataModel(n_locations=4, n_fleet=2, n_orders=3) dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([1, 2, 3])) +dm.set_order_locations(cudf.Series([1, 2, 3], dtype="int32")) solution = routing.Solve(dm, routing.SolverSettings()) if solution.get_status() == 0: diff --git a/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py b/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py index 6e16326d15..d85ec5329b 100644 --- a/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py +++ b/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py @@ -23,7 +23,7 @@ n_fleet = 2 n_orders = 4 -order_locations = cudf.Series([1, 2, 3, 4]) +order_locations = cudf.Series([1, 2, 3, 4], dtype="int32") pickup_indices = cudf.Series([0, 2]) delivery_indices = cudf.Series([1, 3]) demand = cudf.Series([10, -10, 15, -15], dtype="int32") diff --git a/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py b/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py index d85a5174dc..165f6afc1e 100644 --- a/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py +++ b/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py @@ -20,7 +20,7 @@ dm = routing.DataModel(n_locations=4, n_fleet=1, n_orders=3) dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([1, 2, 3])) +dm.set_order_locations(cudf.Series([1, 2, 3], dtype="int32")) solution = routing.Solve(dm, routing.SolverSettings()) diff --git a/.github/skills/cuopt-routing-api-python/resources/examples.md b/.github/skills/cuopt-routing-api-python/resources/examples.md index 4ff694d35a..ee402bb314 100644 --- a/.github/skills/cuopt-routing-api-python/resources/examples.md +++ b/.github/skills/cuopt-routing-api-python/resources/examples.md @@ -28,7 +28,7 @@ cost_matrix = cudf.DataFrame([ transit_time_matrix = cost_matrix.copy(deep=True) # Order data (customers 1-5) -order_locations = cudf.Series([1, 2, 3, 4, 5]) # Location indices for orders +order_locations = cudf.Series([1, 2, 3, 4, 5], dtype="int32") # Location indices for orders # Demand at each customer (single capacity dimension) demand = cudf.Series([20, 30, 25, 15, 35], dtype="int32") @@ -130,7 +130,7 @@ n_fleet = 2 n_orders = 4 # 2 pickup-delivery pairs = 4 orders # Orders: pickup at loc 1 -> deliver at loc 2, pickup at loc 3 -> deliver at loc 4 -order_locations = cudf.Series([1, 2, 3, 4]) +order_locations = cudf.Series([1, 2, 3, 4], dtype="int32") # Pickup and delivery pairs (indices into order array) # Order 0 (pickup) pairs with Order 1 (delivery) @@ -191,7 +191,7 @@ cost_matrix = cudf.DataFrame([ dm = routing.DataModel(n_locations=4, n_fleet=1, n_orders=3) dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([1, 2, 3])) +dm.set_order_locations(cudf.Series([1, 2, 3], dtype="int32")) solution = routing.Solve(dm, routing.SolverSettings()) @@ -219,7 +219,7 @@ n_fleet = 2 dm = routing.DataModel(n_locations=6, n_fleet=n_fleet, n_orders=4) dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([2, 3, 4, 5])) +dm.set_order_locations(cudf.Series([2, 3, 4, 5], dtype="int32")) # Vehicle 0 starts/ends at depot 0, Vehicle 1 at depot 1 dm.set_vehicle_locations( From d8f0b1b97ccadc55a658924c712d62cf0f3b14f3 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 05:30:37 -0600 Subject: [PATCH 15/35] fix review --- .../assets/mps_solver/model.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py index 0481bcd20e..f20a7a6045 100644 --- a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py @@ -81,9 +81,9 @@ def solve_mps( problem = Problem.readMPS(filepath) print(f"Loaded MPS file: {filepath}") - print(f"Variables: {problem.NumVariables()}") - print(f"Constraints: {problem.NumConstraints()}") - print(f"Is MIP: {problem.IsMIP()}") + print(f"Variables: {problem.NumVariables}") + print(f"Constraints: {problem.NumConstraints}") + print(f"Is MIP: {problem.IsMIP}") # Solver settings settings = SolverSettings() @@ -103,9 +103,9 @@ def solve_mps( solution = { "status": status, "objective": problem.ObjValue, - "num_variables": problem.NumVariables(), - "num_constraints": problem.NumConstraints(), - "is_mip": problem.IsMIP(), + "num_variables": problem.NumVariables, + "num_constraints": problem.NumConstraints, + "is_mip": problem.IsMIP, "mip_gap": mip_gap, } From bcdc76348747f00a4f4133112dc3f4cfedeb5789 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 05:33:17 -0600 Subject: [PATCH 16/35] add details on how to build --- .../cuopt-lp-milp-api-c/assets/README.md | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/README.md index 2d7c5ba4cf..f47c7b05f7 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/README.md +++ b/.github/skills/cuopt-lp-milp-api-c/assets/README.md @@ -11,11 +11,31 @@ LP/MILP C API reference implementations. Use as reference when building new appl | [milp_production_planning](milp_production_planning/) | MILP | Production planning with resource constraints | | [mps_solver](mps_solver/) | LP/MILP | Solve from MPS file via `cuOptReadProblem` | -Build (after setting `INCLUDE_PATH` and `LIB_PATH` to cuOpt): +## Build and run + +Set include and library paths, then build and run. + +**Using conda:** Activate your cuOpt env first (`conda activate cuopt`), then: ```bash -gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o lp_basic/lp_simple lp_basic/lp_simple.c -lcuopt -LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./lp_basic/lp_simple +# Paths from active conda env (CONDA_PREFIX is set when env is activated) +export INCLUDE_PATH="${CONDA_PREFIX}/include" +export LIB_PATH="${CONDA_PREFIX}/lib" +export LD_LIBRARY_PATH="${LIB_PATH}:${LD_LIBRARY_PATH}" + +# Build (from this assets/ directory) +gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o lp_basic/lp_simple lp_basic/lp_simple.c -lcuopt +gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o lp_duals/lp_duals lp_duals/lp_duals.c -lcuopt +gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o milp_basic/milp_simple milp_basic/milp_simple.c -lcuopt +gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o milp_production_planning/milp_production milp_production_planning/milp_production.c -lcuopt +gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o mps_solver/mps_solver mps_solver/mps_solver.c -lcuopt + +# Run +./lp_basic/lp_simple +./lp_duals/lp_duals +./milp_basic/milp_simple +./milp_production_planning/milp_production +./mps_solver/mps_solver mps_solver/data/sample.mps ``` -Each subdirectory has its own README with build and run commands. +Without conda, set `INCLUDE_PATH` and `LIB_PATH` to your cuOpt include and lib directories, then use the same `gcc` and `LD_LIBRARY_PATH` as above. Each subdirectory README has a one-line build/run for that example. From f7e2953c9560efb819817bb0afcfd33aa11556b1 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 05:39:15 -0600 Subject: [PATCH 17/35] fix reviews --- .../skills/cuopt-lp-milp-api-c/assets/README.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/README.md b/.github/skills/cuopt-lp-milp-api-c/assets/README.md index f47c7b05f7..e354988da1 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/README.md +++ b/.github/skills/cuopt-lp-milp-api-c/assets/README.md @@ -23,19 +23,11 @@ export INCLUDE_PATH="${CONDA_PREFIX}/include" export LIB_PATH="${CONDA_PREFIX}/lib" export LD_LIBRARY_PATH="${LIB_PATH}:${LD_LIBRARY_PATH}" -# Build (from this assets/ directory) -gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o lp_basic/lp_simple lp_basic/lp_simple.c -lcuopt -gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o lp_duals/lp_duals lp_duals/lp_duals.c -lcuopt -gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o milp_basic/milp_simple milp_basic/milp_simple.c -lcuopt -gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o milp_production_planning/milp_production milp_production_planning/milp_production.c -lcuopt -gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o mps_solver/mps_solver mps_solver/mps_solver.c -lcuopt - -# Run +# Build and run (from this assets/ directory) — example: lp_basic +gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o lp_basic/lp_simple lp_basic/lp_simple.c -lcuopt ./lp_basic/lp_simple -./lp_duals/lp_duals -./milp_basic/milp_simple -./milp_production_planning/milp_production -./mps_solver/mps_solver mps_solver/data/sample.mps ``` +For the other examples, use the same pattern (e.g. `lp_duals/lp_duals.c` → `lp_duals/lp_duals`). `mps_solver` takes an MPS file path: `./mps_solver mps_solver/data/sample.mps`. + Without conda, set `INCLUDE_PATH` and `LIB_PATH` to your cuOpt include and lib directories, then use the same `gcc` and `LD_LIBRARY_PATH` as above. Each subdirectory README has a one-line build/run for that example. From ab758293fac4bcc9f2105f6c320bf5965327b3fc Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 05:41:42 -0600 Subject: [PATCH 18/35] test cli --- .github/skills/cuopt-lp-milp-api-cli/assets/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/README.md b/.github/skills/cuopt-lp-milp-api-cli/assets/README.md index 502d37fd2f..8680eb9e38 100644 --- a/.github/skills/cuopt-lp-milp-api-cli/assets/README.md +++ b/.github/skills/cuopt-lp-milp-api-cli/assets/README.md @@ -9,3 +9,13 @@ Sample MPS files for use with `cuopt_cli`. Use as reference; do not edit in plac | [lp_simple](lp_simple/) | LP | Minimal LP (PROD_X, PROD_Y, two constraints) | **Run:** From each subdir or with path: `cuopt_cli lp_simple/sample.mps` (or `cuopt_cli production.mps`, etc.). See the skill for options (`--time-limit`, `--mip-relative-tolerance`, etc.). + +## Test CLI + +With conda env `cuopt` activated, from this `assets/` directory: + +```bash +cuopt_cli lp_simple/sample.mps --time-limit 10 +``` + +Use the same pattern for the other MPS files; for MILP, add e.g. `--mip-relative-gap 0.01`. From 98b6ac6a634880fbbc002b2d7bf3a8c7f46cf0f6 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 2 Mar 2026 05:43:56 -0600 Subject: [PATCH 19/35] fix --- .../skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c | 5 +++++ .../assets/milp_production_planning/milp_production.c | 5 +++++ .../cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c | 5 +++++ .../cuopt-lp-milp-api-python/assets/mps_solver/model.py | 4 +++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c index f58efee3f2..a92262d18a 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /* * LP with dual values and reduced costs (C API). * Problem: Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x,y,z >= 0. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c index 963c464f42..093cdc8115 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /* * Production planning MILP (C API): two products, resource limits, maximize profit. * Variables: Product_A (x1), Product_B (x2), both integer, lb 10 and 15. diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c index 5bbfc25098..9aeb6f952a 100644 --- a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c +++ b/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /* * Solve LP/MILP from MPS file (C API). * Usage: mps_solver diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py index f20a7a6045..42a7490398 100644 --- a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py +++ b/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py @@ -169,7 +169,9 @@ def compare_gaps( } if known_optimal is not None: results[gap]["gap_to_optimal"] = ( - (solution["objective"] - known_optimal) / known_optimal * 100 + (solution["objective"] - known_optimal) + / known_optimal + * 100 ) else: results[gap] = {"objective": None, "status": "No solution"} From 7ffa51074b43a60d1d3f4ff1d7a33b72af738b3c Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 02:37:03 -0600 Subject: [PATCH 20/35] add test cases --- .../assets/milp_basic/incumbent_callback.py | 12 ++- ci/test_python.sh | 3 + ci/test_skills_assets.sh | 97 +++++++++++++++++++ 3 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 ci/test_skills_assets.sh diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py index 624734fc9d..1af25cf9d5 100644 --- a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py +++ b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py @@ -13,14 +13,15 @@ class IncumbentCallback(GetSolutionCallback): - def __init__(self): + def __init__(self, user_data): super().__init__() self.n_callbacks = 0 + self.user_data = user_data - def get_solution(self, solution, solution_cost): + def get_solution(self, solution, solution_cost, solution_bound, user_data): self.n_callbacks += 1 - sol = solution.copy_to_host() - cost = solution_cost.copy_to_host()[0] + sol = solution.tolist() if hasattr(solution, "tolist") else list(solution) + cost = float(solution_cost[0]) print(f"Incumbent {self.n_callbacks}: {sol}, cost: {cost:.2f}") @@ -32,8 +33,9 @@ def main(): problem.addConstraint(3 * x + 2 * y <= 190) problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) + user_data = {"source": "incumbent_callback"} settings = SolverSettings() - settings.set_mip_callback(IncumbentCallback()) + settings.set_mip_callback(IncumbentCallback(user_data), user_data) settings.set_parameter(CUOPT_TIME_LIMIT, 30) problem.solve(settings) diff --git a/ci/test_python.sh b/ci/test_python.sh index 0a70e56fa7..885f54328e 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -74,5 +74,8 @@ timeout 20m ./ci/run_cuopt_server_pytests.sh \ --cov-report=xml:"${RAPIDS_COVERAGE_DIR}/cuopt-server-coverage.xml" \ --cov-report=term +rapids-logger "Test .github/skills assets (Python, C, CLI)" +timeout 10m ./ci/test_skills_assets.sh + rapids-logger "Test script exiting with value: $EXITCODE" exit ${EXITCODE} diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh new file mode 100644 index 0000000000..de4100acac --- /dev/null +++ b/ci/test_skills_assets.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Run all assets under .github/skills (Python, C, CLI) as part of conda Python test. +# Python: run each .py from its directory. C: compile and run each .c with libcuopt. +# CLI: run cuopt_cli on each sample .mps in API-CLI skill assets. + +set -euo pipefail + +# Use rapids-logger in CI; fall back to echo for local testing +if command -v rapids-logger &>/dev/null; then + log() { rapids-logger "$*"; } +else + log() { echo "[rapids-logger] $*"; } +fi + +REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +SKILLS_ASSETS="${REPO_ROOT}/.github/skills" +FAILED=() + +if [[ ! -d "${SKILLS_ASSETS}" ]]; then + log "No .github/skills directory found, skipping skills asset tests" + exit 0 +fi + +# ---- Python assets ---- +log "Testing Python assets in .github/skills" +while IFS= read -r -d '' script; do + dir=$(dirname "$script") + name=$(basename "$script") + rel="${script#"$REPO_ROOT/"}" + log "Running Python asset: $rel" + if (cd "$dir" && python "$name"); then + log "PASS: $rel" + else + FAILED+=("$rel") + log "FAIL: $rel" + fi +done < <(find "${SKILLS_ASSETS}" -path "*/assets/*" -name "*.py" -type f -print0 | sort -z) + +# ---- C assets (compile and run; requires CONDA_PREFIX) ---- +if [[ -n "${CONDA_PREFIX:-}" ]]; then + INCLUDE_PATH="${CONDA_PREFIX}/include" + LIB_PATH="${CONDA_PREFIX}/lib" + export LD_LIBRARY_PATH="${LIB_PATH}:${LD_LIBRARY_PATH:-}" + + log "Testing C assets in .github/skills" + while IFS= read -r -d '' cfile; do + dir=$(dirname "$cfile") + base=$(basename "$cfile" .c) + rel="${cfile#"$REPO_ROOT/"}" + binary="${dir}/${base}" + log "Building and running C asset: $rel" + if ! (cd "$dir" && gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt); then + FAILED+=("$rel (build)") + log "FAIL: $rel (build)" + continue + fi + if [[ "$base" == "mps_solver" ]]; then + run_cmd=(./"$base" data/sample.mps) + else + run_cmd=(./"$base") + fi + if (cd "$dir" && "${run_cmd[@]}"); then + log "PASS: $rel" + else + FAILED+=("$rel") + log "FAIL: $rel" + fi + done < <(find "${SKILLS_ASSETS}" -path "*/assets/*" -name "*.c" -type f -print0 | sort -z) +else + log "CONDA_PREFIX not set, skipping C asset tests" +fi + +# ---- CLI assets (cuopt_cli with sample MPS files) ---- +log "Testing CLI assets in .github/skills" +while IFS= read -r -d '' mps; do + rel="${mps#"$REPO_ROOT/"}" + log "Running CLI asset: $rel" + if cuopt_cli "$mps" --time-limit 10; then + log "PASS: $rel" + else + FAILED+=("$rel") + log "FAIL: $rel" + fi +done < <(find "${SKILLS_ASSETS}" -path "*/cuopt-*-api-cli/assets/*" -name "*.mps" -type f -print0 | sort -z) + +if [[ ${#FAILED[@]} -gt 0 ]]; then + log "The following skills assets failed:" + printf '%s\n' "${FAILED[@]}" + exit 1 +fi + +log "All skills assets (Python, C, CLI) passed." +exit 0 From 0b35ef3f81f95d37b2da88ef7c333b8119f66820 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 02:59:49 -0600 Subject: [PATCH 21/35] rename the skills to be generiv --- .github/AGENTS.md | 6 +++--- .../SKILL.md | 4 ++-- .../{cuopt-qp-formulation => qp-formulation}/SKILL.md | 6 +++--- .../SKILL.md | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) rename .github/skills/{cuopt-lp-milp-formulation => lp-milp-formulation}/SKILL.md (99%) rename .github/skills/{cuopt-qp-formulation => qp-formulation}/SKILL.md (86%) rename .github/skills/{cuopt-routing-formulation => routing-formulation}/SKILL.md (84%) diff --git a/.github/AGENTS.md b/.github/AGENTS.md index b330c14cb3..42d971fd9d 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -14,9 +14,9 @@ AI agent skills for NVIDIA cuOpt optimization engine. Skills use a **flat layout ### Common (concepts only; no API code) - `skills/cuopt-installation-common/` — Install: system and environment requirements (concepts only; no install commands or interface) -- `skills/cuopt-lp-milp-formulation/` — LP/MILP: concepts + problem parsing (parameters, constraints, decisions, objective) -- `skills/cuopt-routing-formulation/` — Routing: VRP, TSP, PDP (problem types, data) -- `skills/cuopt-qp-formulation/` — QP: minimize-only, escalate (beta) +- `skills/lp-milp-formulation/` — LP/MILP: concepts + problem parsing (parameters, constraints, decisions, objective) +- `skills/routing-formulation/` — Routing: VRP, TSP, PDP (problem types, data) +- `skills/qp-formulation/` — QP: minimize-only, escalate (beta) - `skills/cuopt-server-common/` — Server: capabilities, workflow ### API (implementation; one interface per skill) diff --git a/.github/skills/cuopt-lp-milp-formulation/SKILL.md b/.github/skills/lp-milp-formulation/SKILL.md similarity index 99% rename from .github/skills/cuopt-lp-milp-formulation/SKILL.md rename to .github/skills/lp-milp-formulation/SKILL.md index 9b4a1d8b9d..8bb878b7e3 100644 --- a/.github/skills/cuopt-lp-milp-formulation/SKILL.md +++ b/.github/skills/lp-milp-formulation/SKILL.md @@ -1,9 +1,9 @@ --- -name: cuopt-lp-milp-formulation +name: lp-milp-formulation description: LP/MILP concepts and going from problem text to formulation. What LP/MILP are, required formulation questions, typical modeling elements, and how to parse problem statements (parameters, constraints, decisions, objective). --- -# cuOpt LP/MILP Formulation +# LP/MILP Formulation Concepts and workflow for going from a problem description to a clear formulation. No API code here. diff --git a/.github/skills/cuopt-qp-formulation/SKILL.md b/.github/skills/qp-formulation/SKILL.md similarity index 86% rename from .github/skills/cuopt-qp-formulation/SKILL.md rename to .github/skills/qp-formulation/SKILL.md index 8a1c535fe7..1d6bad6d08 100644 --- a/.github/skills/cuopt-qp-formulation/SKILL.md +++ b/.github/skills/qp-formulation/SKILL.md @@ -1,9 +1,9 @@ --- -name: cuopt-qp-formulation -description: Quadratic Programming (QP) with cuOpt — problem form and constraints. Domain concepts; no API or interface. QP is beta. +name: qp-formulation +description: Quadratic Programming (QP) — problem form and constraints. Domain concepts; no API or interface. QP is beta. --- -# cuOpt QP Formulation +# QP Formulation Domain concepts for quadratic programming. No API or interface details here. **QP support in cuOpt is currently in beta.** diff --git a/.github/skills/cuopt-routing-formulation/SKILL.md b/.github/skills/routing-formulation/SKILL.md similarity index 84% rename from .github/skills/cuopt-routing-formulation/SKILL.md rename to .github/skills/routing-formulation/SKILL.md index e714437073..1851125934 100644 --- a/.github/skills/cuopt-routing-formulation/SKILL.md +++ b/.github/skills/routing-formulation/SKILL.md @@ -1,9 +1,9 @@ --- -name: cuopt-routing-formulation -description: Vehicle routing (VRP, TSP, PDP) with cuOpt — problem types and data requirements. Domain concepts; no API or interface. +name: routing-formulation +description: Vehicle routing (VRP, TSP, PDP) — problem types and data requirements. Domain concepts; no API or interface. --- -# cuOpt Routing Formulation +# Routing Formulation Domain concepts for vehicle routing. No API or interface details here. From ebd78868278b31e34fa857978366c9dd6a0c89a0 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 03:00:32 -0600 Subject: [PATCH 22/35] fix style --- .../assets/milp_basic/incumbent_callback.py | 6 +++++- ci/test_python.sh | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py index 1af25cf9d5..49e533291c 100644 --- a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py +++ b/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py @@ -20,7 +20,11 @@ def __init__(self, user_data): def get_solution(self, solution, solution_cost, solution_bound, user_data): self.n_callbacks += 1 - sol = solution.tolist() if hasattr(solution, "tolist") else list(solution) + sol = ( + solution.tolist() + if hasattr(solution, "tolist") + else list(solution) + ) cost = float(solution_cost[0]) print(f"Incumbent {self.n_callbacks}: {sol}, cost: {cost:.2f}") diff --git a/ci/test_python.sh b/ci/test_python.sh index 885f54328e..1e703c750a 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 set -euo pipefail From 638fff593b46717534c2d9550c6cccf2935c2677 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 03:26:10 -0600 Subject: [PATCH 23/35] fix server assets --- .../skills/cuopt-server-api-python/SKILL.md | 13 +- .../cuopt-server-api-python/assets/README.md | 14 ++ .../assets/lp_basic/README.md | 10 + .../assets/lp_basic/client.py | 78 ++++++++ .../assets/milp_basic/README.md | 6 + .../assets/milp_basic/client.py | 76 ++++++++ .../assets/pdp_basic/README.md | 6 + .../assets/pdp_basic/client.py | 75 ++++++++ .../assets/vrp_basic/README.md | 10 + .../assets/vrp_basic/client.py | 79 ++++++++ .../assets/vrp_simple/README.md | 6 + .../assets/vrp_simple/client.py | 75 ++++++++ .../resources/lp_milp_examples.md | 176 ------------------ .../resources/routing_examples.md | 160 ---------------- ci/test_skills_assets.sh | 36 +++- 15 files changed, 479 insertions(+), 341 deletions(-) create mode 100644 .github/skills/cuopt-server-api-python/assets/README.md create mode 100644 .github/skills/cuopt-server-api-python/assets/lp_basic/README.md create mode 100644 .github/skills/cuopt-server-api-python/assets/lp_basic/client.py create mode 100644 .github/skills/cuopt-server-api-python/assets/milp_basic/README.md create mode 100644 .github/skills/cuopt-server-api-python/assets/milp_basic/client.py create mode 100644 .github/skills/cuopt-server-api-python/assets/pdp_basic/README.md create mode 100644 .github/skills/cuopt-server-api-python/assets/pdp_basic/client.py create mode 100644 .github/skills/cuopt-server-api-python/assets/vrp_basic/README.md create mode 100644 .github/skills/cuopt-server-api-python/assets/vrp_basic/client.py create mode 100644 .github/skills/cuopt-server-api-python/assets/vrp_simple/README.md create mode 100644 .github/skills/cuopt-server-api-python/assets/vrp_simple/client.py delete mode 100644 .github/skills/cuopt-server-api-python/resources/lp_milp_examples.md delete mode 100644 .github/skills/cuopt-server-api-python/resources/routing_examples.md diff --git a/.github/skills/cuopt-server-api-python/SKILL.md b/.github/skills/cuopt-server-api-python/SKILL.md index abb7da1ea8..0affe6efee 100644 --- a/.github/skills/cuopt-server-api-python/SKILL.md +++ b/.github/skills/cuopt-server-api-python/SKILL.md @@ -62,10 +62,17 @@ Use `travel_time_matrix_data` (not transit_time_matrix_data). Capacities: `[[50, **Validation errors:** Check field names against OpenAPI (`/cuopt.yaml`). Common mistakes: `transit_time_matrix_data` → `travel_time_matrix_data`; capacities per dimension `[[50, 50]]` not per vehicle `[[50], [50]]`. Capture `reqId` and response body for failed requests. -## Examples +## Runnable assets -- [routing_examples.md](resources/routing_examples.md) — VRP/PDP via REST -- [lp_milp_examples.md](resources/lp_milp_examples.md) — LP/MILP via REST +Run from each asset directory (server must be running; scripts exit 0 if server unreachable). All use Python `requests`: + +- [assets/vrp_simple/](assets/vrp_simple/) — Basic VRP (no time windows) +- [assets/vrp_basic/](assets/vrp_basic/) — VRP with time windows +- [assets/pdp_basic/](assets/pdp_basic/) — Pickup and delivery +- [assets/lp_basic/](assets/lp_basic/) — LP via REST (CSR format) +- [assets/milp_basic/](assets/milp_basic/) — MILP via REST + +See [assets/README.md](assets/README.md) for overview. ## Escalate diff --git a/.github/skills/cuopt-server-api-python/assets/README.md b/.github/skills/cuopt-server-api-python/assets/README.md new file mode 100644 index 0000000000..908e916dc9 --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/README.md @@ -0,0 +1,14 @@ +# Server API Python — runnable assets + +REST client examples (Python requests). Each runs against a cuOpt server; if the server is not reachable, the script exits 0 (skip). + +| Asset | Description | +|---------------|-------------| +| `vrp_simple/` | Basic VRP (no time windows) | +| `vrp_basic/` | VRP with time windows | +| `pdp_basic/` | Pickup and delivery (pairs) | +| `lp_basic/` | LP (CSR format) | +| `milp_basic/` | MILP (integer + continuous variables) | + +Start server: `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000` +Env: `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/lp_basic/README.md b/.github/skills/cuopt-server-api-python/assets/lp_basic/README.md new file mode 100644 index 0000000000..572969bf00 --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/lp_basic/README.md @@ -0,0 +1,10 @@ +# LP via REST (maximize 40x + 30y) + +Submit an LP to the cuOpt server (CSR format) and poll for the solution. + +**Requires:** cuOpt server running (e.g. `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000`). + +**Run:** `python client.py` +If the server is not reachable, the script exits 0 (skip). + +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/lp_basic/client.py b/.github/skills/cuopt-server-api-python/assets/lp_basic/client.py new file mode 100644 index 0000000000..272073edba --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/lp_basic/client.py @@ -0,0 +1,78 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: LP request (maximize 40x + 30y s.t. 2x+3y<=240, 4x+2y<=200). Requires cuOpt server running. + +Usage: python client.py + Set CUOPT_SERVER_URL (default http://localhost:8000). Exits 0 if server unreachable (e.g. in CI without server). +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + sys.exit(0) + + payload = { + "csr_constraint_matrix": { + "offsets": [0, 2, 4], + "indices": [0, 1, 0, 1], + "values": [2.0, 3.0, 4.0, 2.0], + }, + "constraint_bounds": { + "upper_bounds": [240.0, 200.0], + "lower_bounds": ["ninf", "ninf"], + }, + "objective_data": { + "coefficients": [40.0, 30.0], + }, + "variable_bounds": { + "upper_bounds": ["inf", "inf"], + "lower_bounds": [0.0, 0.0], + }, + "maximize": True, + "solver_config": { + "time_limit": 60, + }, + } + + response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(30): + response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + result = response.json() + + if "response" in result: + print(f"Status: {result['response'].get('status')}") + print(f"Objective: {result['response'].get('objective_value')}") + print(f"Solution: {result['response'].get('primal_solution')}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-server-api-python/assets/milp_basic/README.md b/.github/skills/cuopt-server-api-python/assets/milp_basic/README.md new file mode 100644 index 0000000000..a548055d82 --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/milp_basic/README.md @@ -0,0 +1,6 @@ +# MILP via REST + +Same problem as LP (maximize 40x + 30y, 2x+3y≤240, 4x+2y≤200) with `variable_types`: first variable integer, second continuous. + +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). Variable types: `continuous`, `integer`, `binary`. diff --git a/.github/skills/cuopt-server-api-python/assets/milp_basic/client.py b/.github/skills/cuopt-server-api-python/assets/milp_basic/client.py new file mode 100644 index 0000000000..059e746668 --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/milp_basic/client.py @@ -0,0 +1,76 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: MILP (same constraints as LP but variable_types: integer, continuous). +Requires cuOpt server running. Exits 0 if server unreachable. +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + sys.exit(0) + + payload = { + "csr_constraint_matrix": { + "offsets": [0, 2, 4], + "indices": [0, 1, 0, 1], + "values": [2.0, 3.0, 4.0, 2.0], + }, + "constraint_bounds": { + "upper_bounds": [240.0, 200.0], + "lower_bounds": ["ninf", "ninf"], + }, + "objective_data": {"coefficients": [40.0, 30.0]}, + "variable_bounds": { + "upper_bounds": ["inf", "inf"], + "lower_bounds": [0.0, 0.0], + }, + "variable_types": ["integer", "continuous"], + "maximize": True, + "solver_config": { + "time_limit": 120, + "tolerances": {"mip_relative_gap": 0.01}, + }, + } + + response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(60): + response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + result = response.json() + + if "response" in result: + print(f"Status: {result['response'].get('status')}") + print(f"Objective: {result['response'].get('objective_value')}") + print(f"Solution: {result['response'].get('primal_solution')}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-server-api-python/assets/pdp_basic/README.md b/.github/skills/cuopt-server-api-python/assets/pdp_basic/README.md new file mode 100644 index 0000000000..6769186a0d --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/pdp_basic/README.md @@ -0,0 +1,6 @@ +# Pickup and delivery (PDP) + +Pickup-delivery pairs: (0,1) and (2,3). Pickup must be visited before the corresponding delivery. + +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/pdp_basic/client.py b/.github/skills/cuopt-server-api-python/assets/pdp_basic/client.py new file mode 100644 index 0000000000..4a62e007c0 --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/pdp_basic/client.py @@ -0,0 +1,75 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: Pickup and delivery (PDP). Pairs (0,1) and (2,3); pickup before delivery. +Requires cuOpt server running. Exits 0 if server unreachable. +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + sys.exit(0) + + payload = { + "cost_matrix_data": { + "data": {"0": [[0, 10, 20, 30, 40], [10, 0, 15, 25, 35], [20, 15, 0, 10, 20], [30, 25, 10, 0, 15], [40, 35, 20, 15, 0]]} + }, + "travel_time_matrix_data": { + "data": {"0": [[0, 10, 20, 30, 40], [10, 0, 15, 25, 35], [20, 15, 0, 10, 20], [30, 25, 10, 0, 15], [40, 35, 20, 15, 0]]} + }, + "task_data": { + "task_locations": [1, 2, 3, 4], + "demand": [[10, -10, 15, -15]], + "pickup_and_delivery_pairs": [[0, 1], [2, 3]], + }, + "fleet_data": { + "vehicle_locations": [[0, 0]], + "capacities": [[50]], + }, + "solver_config": {"time_limit": 10}, + } + + response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(30): + response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + result = response.json() + + if "response" in result: + solver_response = result["response"].get("solver_response", {}) + print(f"Status: {solver_response.get('status')}") + print(f"Cost: {solver_response.get('solution_cost')}") + if "vehicle_data" in solver_response: + for vid, vdata in solver_response["vehicle_data"].items(): + print(f"Vehicle {vid}: {vdata.get('route', [])}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_basic/README.md b/.github/skills/cuopt-server-api-python/assets/vrp_basic/README.md new file mode 100644 index 0000000000..132afdddda --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/vrp_basic/README.md @@ -0,0 +1,10 @@ +# VRP with time windows (REST client) + +Submit a VRP with time windows to the cuOpt server and poll for the solution. + +**Requires:** cuOpt server running (e.g. `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000`). + +**Run:** `python client.py` +If the server is not reachable, the script exits 0 (skip). + +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_basic/client.py b/.github/skills/cuopt-server-api-python/assets/vrp_basic/client.py new file mode 100644 index 0000000000..d828875587 --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/vrp_basic/client.py @@ -0,0 +1,79 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: VRP with time windows. Requires cuOpt server running. + +Usage: python client.py + Set CUOPT_SERVER_URL (default http://localhost:8000). Exits 0 if server unreachable (e.g. in CI without server). +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + sys.exit(0) + + payload = { + "cost_matrix_data": { + "data": {"0": [[0, 10, 15, 20, 25], [10, 0, 12, 18, 22], [15, 12, 0, 10, 15], [20, 18, 10, 0, 8], [25, 22, 15, 8, 0]]} + }, + "travel_time_matrix_data": { + "data": {"0": [[0, 10, 15, 20, 25], [10, 0, 12, 18, 22], [15, 12, 0, 10, 15], [20, 18, 10, 0, 8], [25, 22, 15, 8, 0]]} + }, + "task_data": { + "task_locations": [1, 2, 3, 4], + "demand": [[20, 30, 25, 15]], + "task_time_windows": [[0, 50], [10, 60], [20, 70], [0, 80]], + "service_times": [5, 5, 5, 5], + }, + "fleet_data": { + "vehicle_locations": [[0, 0], [0, 0]], + "capacities": [[100, 100]], + "vehicle_time_windows": [[0, 200], [0, 200]], + }, + "solver_config": {"time_limit": 10}, + } + + response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(30): + response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + result = response.json() + + if "response" in result: + solver_response = result["response"].get("solver_response", {}) + print(f"Status: {solver_response.get('status')}") + print(f"Cost: {solver_response.get('solution_cost')}") + if "vehicle_data" in solver_response: + for vid, vdata in solver_response["vehicle_data"].items(): + print(f"Vehicle {vid}: {vdata.get('route', [])}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_simple/README.md b/.github/skills/cuopt-server-api-python/assets/vrp_simple/README.md new file mode 100644 index 0000000000..f0ed60680a --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/vrp_simple/README.md @@ -0,0 +1,6 @@ +# Basic VRP (no time windows) + +Simple VRP: 4 locations, 3 tasks, 2 vehicles. No time windows. + +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_simple/client.py b/.github/skills/cuopt-server-api-python/assets/vrp_simple/client.py new file mode 100644 index 0000000000..78b636e2c4 --- /dev/null +++ b/.github/skills/cuopt-server-api-python/assets/vrp_simple/client.py @@ -0,0 +1,75 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: Basic VRP (no time windows). 4 locations, 3 tasks, 2 vehicles. +Requires cuOpt server running. Exits 0 if server unreachable. +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + sys.exit(0) + + payload = { + "cost_matrix_data": { + "data": {"0": [[0, 10, 15, 20], [10, 0, 12, 18], [15, 12, 0, 10], [20, 18, 10, 0]]} + }, + "travel_time_matrix_data": { + "data": {"0": [[0, 10, 15, 20], [10, 0, 12, 18], [15, 12, 0, 10], [20, 18, 10, 0]]} + }, + "task_data": { + "task_locations": [1, 2, 3], + "demand": [[10, 15, 20]], + "service_times": [5, 5, 5], + }, + "fleet_data": { + "vehicle_locations": [[0, 0], [0, 0]], + "capacities": [[50, 50]], + }, + "solver_config": {"time_limit": 5}, + } + + response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(30): + response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + result = response.json() + + if "response" in result: + solver_response = result["response"].get("solver_response", {}) + print(f"Status: {solver_response.get('status')}") + print(f"Cost: {solver_response.get('solution_cost')}") + if "vehicle_data" in solver_response: + for vid, vdata in solver_response["vehicle_data"].items(): + print(f"Vehicle {vid}: {vdata.get('route', [])}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.github/skills/cuopt-server-api-python/resources/lp_milp_examples.md b/.github/skills/cuopt-server-api-python/resources/lp_milp_examples.md deleted file mode 100644 index 107bb2490b..0000000000 --- a/.github/skills/cuopt-server-api-python/resources/lp_milp_examples.md +++ /dev/null @@ -1,176 +0,0 @@ -# Server: LP/MILP Examples - -## LP Request (curl) - -```bash -# maximize 40*x + 30*y -# s.t. 2x + 3y <= 240 -# 4x + 2y <= 200 - -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": true, - "solver_config": { - "time_limit": 60 - } - }' | jq -r '.reqId') - -sleep 2 -curl -s "http://localhost:8000/cuopt/solution/$REQID" -H "CLIENT-VERSION: custom" | jq . -``` - -## MILP Request (curl) - -```bash -# Submit MILP request and capture reqId -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0] - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "variable_types": ["integer", "continuous"], - "maximize": true, - "solver_config": { - "time_limit": 120, - "tolerances": { - "mip_relative_gap": 0.01 - } - } - }' | jq -r '.reqId') -# Note: objective_data also supports optional "scalability_factor" and "offset" fields - -# Poll for solution (MILP may take longer than LP) -sleep 3 -curl -s "http://localhost:8000/cuopt/solution/$REQID" -H "CLIENT-VERSION: custom" | jq . -``` - -## LP Request (Python) - -```python -import requests -import time - -SERVER = "http://localhost:8000" -HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} - -payload = { - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0] - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": True, - "solver_config": { - "time_limit": 60 - } -} - -# Submit -response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) -req_id = response.json()["reqId"] -print(f"Submitted: {req_id}") - -# Poll for solution -for _ in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) - result = response.json() - - if "response" in result: - print(f"Status: {result['response'].get('status')}") - print(f"Objective: {result['response'].get('objective_value')}") - print(f"Solution: {result['response'].get('primal_solution')}") - break - time.sleep(1) -``` - -## CSR Matrix Format - -``` -Matrix: [2, 3] (row 0: 2*x0 + 3*x1) - [4, 2] (row 1: 4*x0 + 2*x1) - -CSR format: - offsets: [0, 2, 4] # Row pointers (n_rows + 1) - indices: [0, 1, 0, 1] # Column indices - values: [2.0, 3.0, 4.0, 2.0] # Non-zero values -``` - -## Special Values - -```json -{ - "constraint_bounds": { - "lower_bounds": ["ninf", "ninf"], - "upper_bounds": [100.0, "inf"] - } -} -``` - -## Variable Types - -- `"continuous"` - real-valued -- `"integer"` - integer-valued -- `"binary"` - 0 or 1 only - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | -|---------|------| -| Basic LP (Python) | `docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.py` | -| Basic LP (curl) | `docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.sh` | -| MPS File Input | `docs/cuopt/source/cuopt-server/examples/lp/examples/mps_file_example.py` | -| Warmstart | `docs/cuopt/source/cuopt-server/examples/lp/examples/warmstart_example.py` | -| Basic MILP | `docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.py` | -| Incumbent Callback | `docs/cuopt/source/cuopt-server/examples/milp/examples/incumbent_callback_example.py` | - -These examples are tested by CI (`ci/test_doc_examples.sh`). diff --git a/.github/skills/cuopt-server-api-python/resources/routing_examples.md b/.github/skills/cuopt-server-api-python/resources/routing_examples.md deleted file mode 100644 index 9caf7e67dd..0000000000 --- a/.github/skills/cuopt-server-api-python/resources/routing_examples.md +++ /dev/null @@ -1,160 +0,0 @@ -# Server: Routing Examples - -## Start Server - -```bash -python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 & -sleep 5 -curl http://localhost:8000/cuopt/health -``` - -> **Note:** Using `--ip 0.0.0.0` binds to all interfaces for development convenience; use `--ip 127.0.0.1` or a specific interface in production or untrusted networks. - -## Basic VRP (curl) - -```bash -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "cost_matrix_data": { - "data": {"0": [[0,10,15,20],[10,0,12,18],[15,12,0,10],[20,18,10,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15,20],[10,0,12,18],[15,12,0,10],[20,18,10,0]]} - }, - "task_data": { - "task_locations": [1, 2, 3], - "demand": [[10, 15, 20]], - "service_times": [5, 5, 5] - }, - "fleet_data": { - "vehicle_locations": [[0, 0], [0, 0]], - "capacities": [[50, 50]] - }, - "solver_config": {"time_limit": 5} - }' | jq -r '.reqId') - -curl -s "http://localhost:8000/cuopt/solution/$REQID" -H "CLIENT-VERSION: custom" | jq . -``` - -## VRP with Time Windows (Python) - -```python -import requests -import time - -SERVER = "http://localhost:8000" -HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} - -payload = { - "cost_matrix_data": { - "data": {"0": [[0,10,15,20,25],[10,0,12,18,22],[15,12,0,10,15],[20,18,10,0,8],[25,22,15,8,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15,20,25],[10,0,12,18,22],[15,12,0,10,15],[20,18,10,0,8],[25,22,15,8,0]]} - }, - "task_data": { - "task_locations": [1, 2, 3, 4], - "demand": [[20, 30, 25, 15]], - "task_time_windows": [[0, 50], [10, 60], [20, 70], [0, 80]], - "service_times": [5, 5, 5, 5] - }, - "fleet_data": { - "vehicle_locations": [[0, 0], [0, 0]], - "capacities": [[100, 100]], - "vehicle_time_windows": [[0, 200], [0, 200]] - }, - "solver_config": { - "time_limit": 10 - } -} - -# Submit -response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) -req_id = response.json()["reqId"] -print(f"Submitted: {req_id}") - -# Poll for solution -for attempt in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) - result = response.json() - - if "response" in result: - solver_response = result["response"].get("solver_response", {}) - print(f"Status: {solver_response.get('status')}") - print(f"Cost: {solver_response.get('solution_cost')}") - if "vehicle_data" in solver_response: - for vid, vdata in solver_response["vehicle_data"].items(): - print(f"Vehicle {vid}: {vdata.get('route', [])}") - break - time.sleep(1) -``` - -## Pickup and Delivery (curl) - -```bash -curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "cost_matrix_data": { - "data": {"0": [[0,10,20,30,40],[10,0,15,25,35],[20,15,0,10,20],[30,25,10,0,15],[40,35,20,15,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,20,30,40],[10,0,15,25,35],[20,15,0,10,20],[30,25,10,0,15],[40,35,20,15,0]]} - }, - "task_data": { - "task_locations": [1, 2, 3, 4], - "demand": [[10, -10, 15, -15]], - "pickup_and_delivery_pairs": [[0, 1], [2, 3]] - }, - "fleet_data": { - "vehicle_locations": [[0, 0]], - "capacities": [[50]] - }, - "solver_config": {"time_limit": 10} - }' | jq . -``` - -## Terminology: Python vs REST - -| Python API | REST Server | -|------------|-------------| -| `order_locations` | `task_locations` | -| `set_order_time_windows()` | `task_time_windows` | -| `set_order_service_times()` | `service_times` | -| `add_transit_time_matrix()` | `travel_time_matrix_data` | -| `set_pickup_delivery_pairs()` | `pickup_and_delivery_pairs` | - -## Common Mistakes - -```json -// ❌ WRONG field name -"transit_time_matrix_data": {...} - -// ✅ CORRECT -"travel_time_matrix_data": {...} -``` - -```json -// ❌ WRONG capacity format (per vehicle) -"capacities": [[50], [50]] - -// ✅ CORRECT (per dimension across vehicles) -"capacities": [[50, 50]] -``` - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | -|---------|------| -| Basic Routing (Python) | `docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.py` | -| Basic Routing (curl) | `docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.sh` | -| Initial Solution | `docs/cuopt/source/cuopt-server/examples/routing/examples/initial_solution_example.py` | - -These examples are tested by CI (`ci/test_doc_examples.sh`). diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh index de4100acac..40fbee2516 100644 --- a/ci/test_skills_assets.sh +++ b/ci/test_skills_assets.sh @@ -1,10 +1,11 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # Run all assets under .github/skills (Python, C, CLI) as part of conda Python test. -# Python: run each .py from its directory. C: compile and run each .c with libcuopt. +# Python: run each .py from its directory (server API clients need server on port 8000). +# C: compile and run each .c with libcuopt. # CLI: run cuopt_cli on each sample .mps in API-CLI skill assets. set -euo pipefail @@ -19,12 +20,43 @@ fi REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" SKILLS_ASSETS="${REPO_ROOT}/.github/skills" FAILED=() +SERVER_PID="" if [[ ! -d "${SKILLS_ASSETS}" ]]; then log "No .github/skills directory found, skipping skills asset tests" exit 0 fi +# ---- Start cuOpt server for server API Python assets (port 8000) ---- +start_server() { + if ! python -c "import cuopt_server" 2>/dev/null; then + log "cuopt_server not available, server API assets will skip" + return + fi + python -m cuopt_server.cuopt_service --ip 127.0.0.1 --port 8000 &>/dev/null & + SERVER_PID=$! + for _ in {1..30}; do + if curl -s -o /dev/null http://127.0.0.1:8000/cuopt/health 2>/dev/null; then + log "cuOpt server started (port 8000) for server API assets" + return + fi + sleep 1 + done + log "cuOpt server did not become ready; server API assets will skip" + kill "${SERVER_PID}" 2>/dev/null || true + SERVER_PID="" +} +stop_server() { + if [[ -n "${SERVER_PID}" ]] && kill -0 "${SERVER_PID}" 2>/dev/null; then + log "Stopping cuOpt server (PID ${SERVER_PID})" + kill "${SERVER_PID}" 2>/dev/null || true + wait "${SERVER_PID}" 2>/dev/null || true + SERVER_PID="" + fi +} +trap stop_server EXIT +start_server + # ---- Python assets ---- log "Testing Python assets in .github/skills" while IFS= read -r -d '' script; do From d442a2638dbc4f32f3cfe36821f1e1b1b0d7adc1 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 03:27:44 -0600 Subject: [PATCH 24/35] test --- .../cuopt-server-api-python/assets/README.md | 2 +- .../assets/lp_basic/README.md | 2 +- .../assets/lp_basic/client.py | 12 +++++-- .../assets/milp_basic/README.md | 2 +- .../assets/milp_basic/client.py | 12 +++++-- .../assets/pdp_basic/README.md | 2 +- .../assets/pdp_basic/client.py | 32 ++++++++++++++++--- .../assets/vrp_basic/README.md | 2 +- .../assets/vrp_basic/client.py | 32 ++++++++++++++++--- .../assets/vrp_simple/README.md | 2 +- .../assets/vrp_simple/client.py | 30 ++++++++++++++--- 11 files changed, 103 insertions(+), 27 deletions(-) diff --git a/.github/skills/cuopt-server-api-python/assets/README.md b/.github/skills/cuopt-server-api-python/assets/README.md index 908e916dc9..1389f3eb7b 100644 --- a/.github/skills/cuopt-server-api-python/assets/README.md +++ b/.github/skills/cuopt-server-api-python/assets/README.md @@ -10,5 +10,5 @@ REST client examples (Python requests). Each runs against a cuOpt server; if the | `lp_basic/` | LP (CSR format) | | `milp_basic/` | MILP (integer + continuous variables) | -Start server: `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000` +Start server: `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000` Env: `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/lp_basic/README.md b/.github/skills/cuopt-server-api-python/assets/lp_basic/README.md index 572969bf00..34c10fb350 100644 --- a/.github/skills/cuopt-server-api-python/assets/lp_basic/README.md +++ b/.github/skills/cuopt-server-api-python/assets/lp_basic/README.md @@ -4,7 +4,7 @@ Submit an LP to the cuOpt server (CSR format) and poll for the solution. **Requires:** cuOpt server running (e.g. `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000`). -**Run:** `python client.py` +**Run:** `python client.py` If the server is not reachable, the script exits 0 (skip). **Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/lp_basic/client.py b/.github/skills/cuopt-server-api-python/assets/lp_basic/client.py index 272073edba..bca7b15295 100644 --- a/.github/skills/cuopt-server-api-python/assets/lp_basic/client.py +++ b/.github/skills/cuopt-server-api-python/assets/lp_basic/client.py @@ -28,7 +28,9 @@ def server_ok(): def main(): if not server_ok(): - print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) sys.exit(0) payload = { @@ -54,13 +56,17 @@ def main(): }, } - response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) response.raise_for_status() req_id = response.json()["reqId"] print(f"Submitted: {req_id}") for _ in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) result = response.json() if "response" in result: diff --git a/.github/skills/cuopt-server-api-python/assets/milp_basic/README.md b/.github/skills/cuopt-server-api-python/assets/milp_basic/README.md index a548055d82..e490840557 100644 --- a/.github/skills/cuopt-server-api-python/assets/milp_basic/README.md +++ b/.github/skills/cuopt-server-api-python/assets/milp_basic/README.md @@ -2,5 +2,5 @@ Same problem as LP (maximize 40x + 30y, 2x+3y≤240, 4x+2y≤200) with `variable_types`: first variable integer, second continuous. -**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). **Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). Variable types: `continuous`, `integer`, `binary`. diff --git a/.github/skills/cuopt-server-api-python/assets/milp_basic/client.py b/.github/skills/cuopt-server-api-python/assets/milp_basic/client.py index 059e746668..1c18de60e9 100644 --- a/.github/skills/cuopt-server-api-python/assets/milp_basic/client.py +++ b/.github/skills/cuopt-server-api-python/assets/milp_basic/client.py @@ -26,7 +26,9 @@ def server_ok(): def main(): if not server_ok(): - print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) sys.exit(0) payload = { @@ -52,13 +54,17 @@ def main(): }, } - response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) response.raise_for_status() req_id = response.json()["reqId"] print(f"Submitted: {req_id}") for _ in range(60): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) result = response.json() if "response" in result: diff --git a/.github/skills/cuopt-server-api-python/assets/pdp_basic/README.md b/.github/skills/cuopt-server-api-python/assets/pdp_basic/README.md index 6769186a0d..ca6c174c6c 100644 --- a/.github/skills/cuopt-server-api-python/assets/pdp_basic/README.md +++ b/.github/skills/cuopt-server-api-python/assets/pdp_basic/README.md @@ -2,5 +2,5 @@ Pickup-delivery pairs: (0,1) and (2,3). Pickup must be visited before the corresponding delivery. -**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). **Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/pdp_basic/client.py b/.github/skills/cuopt-server-api-python/assets/pdp_basic/client.py index 4a62e007c0..cad4d3bdb1 100644 --- a/.github/skills/cuopt-server-api-python/assets/pdp_basic/client.py +++ b/.github/skills/cuopt-server-api-python/assets/pdp_basic/client.py @@ -26,15 +26,33 @@ def server_ok(): def main(): if not server_ok(): - print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) sys.exit(0) payload = { "cost_matrix_data": { - "data": {"0": [[0, 10, 20, 30, 40], [10, 0, 15, 25, 35], [20, 15, 0, 10, 20], [30, 25, 10, 0, 15], [40, 35, 20, 15, 0]]} + "data": { + "0": [ + [0, 10, 20, 30, 40], + [10, 0, 15, 25, 35], + [20, 15, 0, 10, 20], + [30, 25, 10, 0, 15], + [40, 35, 20, 15, 0], + ] + } }, "travel_time_matrix_data": { - "data": {"0": [[0, 10, 20, 30, 40], [10, 0, 15, 25, 35], [20, 15, 0, 10, 20], [30, 25, 10, 0, 15], [40, 35, 20, 15, 0]]} + "data": { + "0": [ + [0, 10, 20, 30, 40], + [10, 0, 15, 25, 35], + [20, 15, 0, 10, 20], + [30, 25, 10, 0, 15], + [40, 35, 20, 15, 0], + ] + } }, "task_data": { "task_locations": [1, 2, 3, 4], @@ -48,13 +66,17 @@ def main(): "solver_config": {"time_limit": 10}, } - response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) response.raise_for_status() req_id = response.json()["reqId"] print(f"Submitted: {req_id}") for _ in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) result = response.json() if "response" in result: diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_basic/README.md b/.github/skills/cuopt-server-api-python/assets/vrp_basic/README.md index 132afdddda..84b46f7240 100644 --- a/.github/skills/cuopt-server-api-python/assets/vrp_basic/README.md +++ b/.github/skills/cuopt-server-api-python/assets/vrp_basic/README.md @@ -4,7 +4,7 @@ Submit a VRP with time windows to the cuOpt server and poll for the solution. **Requires:** cuOpt server running (e.g. `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000`). -**Run:** `python client.py` +**Run:** `python client.py` If the server is not reachable, the script exits 0 (skip). **Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_basic/client.py b/.github/skills/cuopt-server-api-python/assets/vrp_basic/client.py index d828875587..9285eb05cd 100644 --- a/.github/skills/cuopt-server-api-python/assets/vrp_basic/client.py +++ b/.github/skills/cuopt-server-api-python/assets/vrp_basic/client.py @@ -28,15 +28,33 @@ def server_ok(): def main(): if not server_ok(): - print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) sys.exit(0) payload = { "cost_matrix_data": { - "data": {"0": [[0, 10, 15, 20, 25], [10, 0, 12, 18, 22], [15, 12, 0, 10, 15], [20, 18, 10, 0, 8], [25, 22, 15, 8, 0]]} + "data": { + "0": [ + [0, 10, 15, 20, 25], + [10, 0, 12, 18, 22], + [15, 12, 0, 10, 15], + [20, 18, 10, 0, 8], + [25, 22, 15, 8, 0], + ] + } }, "travel_time_matrix_data": { - "data": {"0": [[0, 10, 15, 20, 25], [10, 0, 12, 18, 22], [15, 12, 0, 10, 15], [20, 18, 10, 0, 8], [25, 22, 15, 8, 0]]} + "data": { + "0": [ + [0, 10, 15, 20, 25], + [10, 0, 12, 18, 22], + [15, 12, 0, 10, 15], + [20, 18, 10, 0, 8], + [25, 22, 15, 8, 0], + ] + } }, "task_data": { "task_locations": [1, 2, 3, 4], @@ -52,13 +70,17 @@ def main(): "solver_config": {"time_limit": 10}, } - response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) response.raise_for_status() req_id = response.json()["reqId"] print(f"Submitted: {req_id}") for _ in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) result = response.json() if "response" in result: diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_simple/README.md b/.github/skills/cuopt-server-api-python/assets/vrp_simple/README.md index f0ed60680a..f9de54a24c 100644 --- a/.github/skills/cuopt-server-api-python/assets/vrp_simple/README.md +++ b/.github/skills/cuopt-server-api-python/assets/vrp_simple/README.md @@ -2,5 +2,5 @@ Simple VRP: 4 locations, 3 tasks, 2 vehicles. No time windows. -**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). **Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_simple/client.py b/.github/skills/cuopt-server-api-python/assets/vrp_simple/client.py index 78b636e2c4..35f37f5c72 100644 --- a/.github/skills/cuopt-server-api-python/assets/vrp_simple/client.py +++ b/.github/skills/cuopt-server-api-python/assets/vrp_simple/client.py @@ -26,15 +26,31 @@ def server_ok(): def main(): if not server_ok(): - print("Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000") + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) sys.exit(0) payload = { "cost_matrix_data": { - "data": {"0": [[0, 10, 15, 20], [10, 0, 12, 18], [15, 12, 0, 10], [20, 18, 10, 0]]} + "data": { + "0": [ + [0, 10, 15, 20], + [10, 0, 12, 18], + [15, 12, 0, 10], + [20, 18, 10, 0], + ] + } }, "travel_time_matrix_data": { - "data": {"0": [[0, 10, 15, 20], [10, 0, 12, 18], [15, 12, 0, 10], [20, 18, 10, 0]]} + "data": { + "0": [ + [0, 10, 15, 20], + [10, 0, 12, 18], + [15, 12, 0, 10], + [20, 18, 10, 0], + ] + } }, "task_data": { "task_locations": [1, 2, 3], @@ -48,13 +64,17 @@ def main(): "solver_config": {"time_limit": 5}, } - response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) response.raise_for_status() req_id = response.json()["reqId"] print(f"Submitted: {req_id}") for _ in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) result = response.json() if "response" in result: From 7adc38229483ce8faad3999974fec16cfe9c89dd Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 03:29:06 -0600 Subject: [PATCH 25/35] fix --- ci/test_skills_assets.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh index 40fbee2516..7cb39ff8a1 100644 --- a/ci/test_skills_assets.sh +++ b/ci/test_skills_assets.sh @@ -83,7 +83,6 @@ if [[ -n "${CONDA_PREFIX:-}" ]]; then dir=$(dirname "$cfile") base=$(basename "$cfile" .c) rel="${cfile#"$REPO_ROOT/"}" - binary="${dir}/${base}" log "Building and running C asset: $rel" if ! (cd "$dir" && gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt); then FAILED+=("$rel (build)") From adef0f6d007581eb743ad86bdc6d3d066dfce1a3 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 08:40:38 -0600 Subject: [PATCH 26/35] address review comment --- .github/skills/cuopt-user-rules/SKILL.md | 2 +- .github/skills/lp-milp-formulation/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/skills/cuopt-user-rules/SKILL.md b/.github/skills/cuopt-user-rules/SKILL.md index 5a71c2f140..cb5c3c6fc7 100644 --- a/.github/skills/cuopt-user-rules/SKILL.md +++ b/.github/skills/cuopt-user-rules/SKILL.md @@ -103,7 +103,7 @@ After providing a solution, guide the user to verify: **Always end with a Result summary** that includes at least: - Solver status (e.g. Optimal, FeasibleFound, SUCCESS). -- **Objective value with highlight** — easy to spot (bold or code block). Example: **Objective value (min total cost): 42 350** or `Objective value: 42350`. +- **Objective value with highlight** — easy to spot (bold or code block). Example: **Objective value (min total cost):** <value> or `Objective value: `. - Briefly what the objective represents (e.g. total cost, total profit). Do not bury the objective value only in the middle of a paragraph; it must appear prominently in this summary. Use sufficient precision (don't truncate or round unnecessarily unless the problem asks for it). diff --git a/.github/skills/lp-milp-formulation/SKILL.md b/.github/skills/lp-milp-formulation/SKILL.md index 8bb878b7e3..30ea1e6a1e 100644 --- a/.github/skills/lp-milp-formulation/SKILL.md +++ b/.github/skills/lp-milp-formulation/SKILL.md @@ -124,4 +124,4 @@ When the user gives **problem text**, classify every sentence and then summarize Result: Parameters = 3 factories, 500 units target. Constraints = produce exactly 500 (implicit from "plans to produce"). Decisions = production allocation across factories, overtime amounts. Objective = minimize cost. -**Implicit-objective example:** "Try to determine the monthly production plan" with workshop hour costs (80/20 yuan), inspection/sales costs (30/50 per unit), and no stated "minimize/maximize" → **Objective is implicit: minimize total cost** (workshop + inspection/sales + any overtime). Always state it: "The objective is to minimize total cost." +**Implicit-objective example:** A problem that asks to "determine the production plan" (or similar) and gives cost components (e.g. workshop, inspection, sales) but does not state "minimize" or "maximize" → **Objective is implicit: minimize total cost**. Always state it explicitly: "The objective is to minimize total cost." From 9515f5a084663abbb7af044fa58210e6ad23cbeb Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 10:51:47 -0600 Subject: [PATCH 27/35] fix test script --- ci/test_skills_assets.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ci/test_skills_assets.sh diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh old mode 100644 new mode 100755 From 5a40adbc4af8db124d77d11efb2ba79a4ee326f7 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 11:47:54 -0600 Subject: [PATCH 28/35] move skills to root and adopt good features to support multiple agents from HF --- .claude-plugin/marketplace.json | 120 ++++++++++++++++++ .claude/AGENTS.md | 2 +- .claude/skills | 2 +- .cursor-plugin/plugin.json | 22 ++++ .cursor/AGENTS.md | 2 +- .cursor/skills | 2 +- .github/AGENTS.md | 47 ------- .github/CODEOWNERS | 2 +- .github/workflows/pr.yaml | 8 +- .opencode/AGENTS.md | 2 +- .opencode/skills | 2 +- .pre-commit-config.yaml | 6 + AGENTS.md | 49 ++++++- agents/AGENTS.md | 59 +++++++++ ci/test_python.sh | 2 +- ci/test_skills_assets.sh | 12 +- ci/utils/validate_skills.sh | 68 ++++++++++ gemini-extension.json | 6 + .../cuopt-developer/SKILL.md | 0 .../cuopt-installation-api-c/SKILL.md | 0 .../resources/verification_examples.md | 0 .../cuopt-installation-api-python/SKILL.md | 0 .../resources/verification_examples.md | 0 .../cuopt-installation-common/SKILL.md | 0 .../cuopt-installation-developer/SKILL.md | 0 .../cuopt-lp-milp-api-c/SKILL.md | 0 .../cuopt-lp-milp-api-c/assets/README.md | 0 .../assets/lp_basic/README.md | 0 .../assets/lp_basic/lp_simple.c | 0 .../assets/lp_duals/README.md | 0 .../assets/lp_duals/lp_duals.c | 0 .../assets/lp_warmstart/README.md | 0 .../assets/milp_basic/README.md | 0 .../assets/milp_basic/milp_simple.c | 0 .../assets/milp_production_planning/README.md | 0 .../milp_production.c | 0 .../assets/mps_solver/README.md | 0 .../assets/mps_solver/data/sample.mps | 0 .../assets/mps_solver/mps_solver.c | 0 .../cuopt-lp-milp-api-c/resources/examples.md | 0 .../cuopt-lp-milp-api-cli/SKILL.md | 0 .../cuopt-lp-milp-api-cli/assets/README.md | 0 .../assets/lp_production/README.md | 0 .../assets/lp_production/production.mps | 0 .../assets/lp_simple/README.md | 0 .../assets/lp_simple/sample.mps | 0 .../assets/milp_facility/README.md | 0 .../assets/milp_facility/facility.mps | 0 .../cuopt-lp-milp-api-python/SKILL.md | 0 .../cuopt-lp-milp-api-python/assets/README.md | 0 .../assets/lp_basic/README.md | 0 .../assets/lp_basic/model.py | 0 .../assets/lp_duals/README.md | 0 .../assets/lp_duals/model.py | 0 .../assets/lp_warmstart/README.md | 0 .../assets/lp_warmstart/model.py | 0 .../assets/milp_basic/README.md | 0 .../assets/milp_basic/incumbent_callback.py | 0 .../assets/milp_basic/model.py | 0 .../assets/milp_production_planning/README.md | 0 .../assets/milp_production_planning/model.py | 0 .../assets/mps_solver/README.md | 0 .../assets/mps_solver/data/README.md | 0 .../assets/mps_solver/data/sample.mps | 0 .../assets/mps_solver/model.py | 0 .../assets/mps_solver/results.md | 0 .../skills => skills}/cuopt-qp-api-c/SKILL.md | 0 .../cuopt-qp-api-c/assets/README.md | 0 .../cuopt-qp-api-cli/SKILL.md | 0 .../cuopt-qp-api-cli/assets/README.md | 0 .../cuopt-qp-api-python/SKILL.md | 0 .../cuopt-qp-api-python/assets/README.md | 0 .../assets/least_squares/README.md | 0 .../assets/least_squares/model.py | 0 .../assets/maximization_workaround/README.md | 0 .../assets/maximization_workaround/model.py | 0 .../assets/portfolio/README.md | 0 .../assets/portfolio/model.py | 0 .../cuopt-qp-api-python/resources/examples.md | 0 .../cuopt-routing-api-python/SKILL.md | 0 .../cuopt-routing-api-python/assets/README.md | 0 .../assets/pdp_basic/README.md | 0 .../assets/pdp_basic/model.py | 0 .../assets/vrp_basic/README.md | 0 .../assets/vrp_basic/model.py | 0 .../resources/examples.md | 0 .../resources/server_examples.md | 0 .../cuopt-server-api-python/SKILL.md | 0 .../cuopt-server-api-python/assets/README.md | 0 .../assets/lp_basic/README.md | 0 .../assets/lp_basic/client.py | 0 .../assets/milp_basic/README.md | 0 .../assets/milp_basic/client.py | 0 .../assets/pdp_basic/README.md | 0 .../assets/pdp_basic/client.py | 0 .../assets/vrp_basic/README.md | 0 .../assets/vrp_basic/client.py | 0 .../assets/vrp_simple/README.md | 0 .../assets/vrp_simple/client.py | 0 .../cuopt-server-common/SKILL.md | 0 .../cuopt-user-rules/SKILL.md | 0 .../lp-milp-formulation/SKILL.md | 0 .../skills => skills}/qp-formulation/SKILL.md | 0 .../routing-formulation/SKILL.md | 0 104 files changed, 343 insertions(+), 70 deletions(-) create mode 100644 .claude-plugin/marketplace.json create mode 100644 .cursor-plugin/plugin.json delete mode 100644 .github/AGENTS.md create mode 100644 agents/AGENTS.md create mode 100644 ci/utils/validate_skills.sh create mode 100644 gemini-extension.json rename {.github/skills => skills}/cuopt-developer/SKILL.md (100%) rename {.github/skills => skills}/cuopt-installation-api-c/SKILL.md (100%) rename {.github/skills => skills}/cuopt-installation-api-c/resources/verification_examples.md (100%) rename {.github/skills => skills}/cuopt-installation-api-python/SKILL.md (100%) rename {.github/skills => skills}/cuopt-installation-api-python/resources/verification_examples.md (100%) rename {.github/skills => skills}/cuopt-installation-common/SKILL.md (100%) rename {.github/skills => skills}/cuopt-installation-developer/SKILL.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/SKILL.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/lp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/lp_duals/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/milp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/mps_solver/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-c/resources/examples.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-cli/SKILL.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-cli/assets/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-cli/assets/lp_production/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-cli/assets/lp_production/production.mps (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-cli/assets/lp_simple/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-cli/assets/milp_facility/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/SKILL.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/lp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/lp_basic/model.py (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/lp_duals/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/lp_duals/model.py (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/milp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/milp_basic/model.py (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/mps_solver/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/mps_solver/model.py (100%) rename {.github/skills => skills}/cuopt-lp-milp-api-python/assets/mps_solver/results.md (100%) rename {.github/skills => skills}/cuopt-qp-api-c/SKILL.md (100%) rename {.github/skills => skills}/cuopt-qp-api-c/assets/README.md (100%) rename {.github/skills => skills}/cuopt-qp-api-cli/SKILL.md (100%) rename {.github/skills => skills}/cuopt-qp-api-cli/assets/README.md (100%) rename {.github/skills => skills}/cuopt-qp-api-python/SKILL.md (100%) rename {.github/skills => skills}/cuopt-qp-api-python/assets/README.md (100%) rename {.github/skills => skills}/cuopt-qp-api-python/assets/least_squares/README.md (100%) rename {.github/skills => skills}/cuopt-qp-api-python/assets/least_squares/model.py (100%) rename {.github/skills => skills}/cuopt-qp-api-python/assets/maximization_workaround/README.md (100%) rename {.github/skills => skills}/cuopt-qp-api-python/assets/maximization_workaround/model.py (100%) rename {.github/skills => skills}/cuopt-qp-api-python/assets/portfolio/README.md (100%) rename {.github/skills => skills}/cuopt-qp-api-python/assets/portfolio/model.py (100%) rename {.github/skills => skills}/cuopt-qp-api-python/resources/examples.md (100%) rename {.github/skills => skills}/cuopt-routing-api-python/SKILL.md (100%) rename {.github/skills => skills}/cuopt-routing-api-python/assets/README.md (100%) rename {.github/skills => skills}/cuopt-routing-api-python/assets/pdp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-routing-api-python/assets/pdp_basic/model.py (100%) rename {.github/skills => skills}/cuopt-routing-api-python/assets/vrp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-routing-api-python/assets/vrp_basic/model.py (100%) rename {.github/skills => skills}/cuopt-routing-api-python/resources/examples.md (100%) rename {.github/skills => skills}/cuopt-routing-api-python/resources/server_examples.md (100%) rename {.github/skills => skills}/cuopt-server-api-python/SKILL.md (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/README.md (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/lp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/lp_basic/client.py (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/milp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/milp_basic/client.py (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/pdp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/pdp_basic/client.py (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/vrp_basic/README.md (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/vrp_basic/client.py (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/vrp_simple/README.md (100%) rename {.github/skills => skills}/cuopt-server-api-python/assets/vrp_simple/client.py (100%) rename {.github/skills => skills}/cuopt-server-common/SKILL.md (100%) rename {.github/skills => skills}/cuopt-user-rules/SKILL.md (100%) rename {.github/skills => skills}/lp-milp-formulation/SKILL.md (100%) rename {.github/skills => skills}/qp-formulation/SKILL.md (100%) rename {.github/skills => skills}/routing-formulation/SKILL.md (100%) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000000..45172e5a19 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,120 @@ +{ + "name": "nvidia-cuopt-skills", + "owner": { + "name": "NVIDIA" + }, + "metadata": { + "description": "Agent skills for NVIDIA cuOpt: routing (VRP, TSP, PDP), LP/MILP/QP, installation (Python/C/developer), and REST server.", + "version": "1.0.0" + }, + "plugins": [ + { + "name": "cuopt-user-rules", + "source": "./skills/cuopt-user-rules", + "skills": "./", + "description": "Base behavior rules for using NVIDIA cuOpt. Read first when helping users with cuOpt (routing, LP/MILP, QP, installation, server)." + }, + { + "name": "cuopt-developer", + "source": "./skills/cuopt-developer", + "skills": "./", + "description": "Contribute to NVIDIA cuOpt codebase including C++/CUDA, Python, server, docs, and CI. Use when the user wants to modify solver internals, add features, submit PRs, or understand the codebase architecture." + }, + { + "name": "cuopt-installation-common", + "source": "./skills/cuopt-installation-common", + "skills": "./", + "description": "Install cuOpt — system and environment requirements only. Domain concepts; no install commands or interface guidance." + }, + { + "name": "cuopt-installation-api-python", + "source": "./skills/cuopt-installation-api-python", + "skills": "./", + "description": "Install cuOpt for Python — pip, conda, Docker, verification. Use when the user is installing or verifying the Python API." + }, + { + "name": "cuopt-installation-api-c", + "source": "./skills/cuopt-installation-api-c", + "skills": "./", + "description": "Install cuOpt for C — conda, locate lib/headers, verification. Use when the user is installing or verifying the C API." + }, + { + "name": "cuopt-installation-developer", + "source": "./skills/cuopt-installation-developer", + "skills": "./", + "description": "Developer installation — build cuOpt from source, run tests. Use when the user wants to set up a dev environment to contribute or modify cuOpt." + }, + { + "name": "lp-milp-formulation", + "source": "./skills/lp-milp-formulation", + "skills": "./", + "description": "LP/MILP concepts and going from problem text to formulation. What LP/MILP are, required formulation questions, typical modeling elements, and how to parse problem statements." + }, + { + "name": "cuopt-lp-milp-api-python", + "source": "./skills/cuopt-lp-milp-api-python", + "skills": "./", + "description": "Solve LP and MILP with the Python API. Use when the user asks about optimization with linear constraints, integer variables, scheduling, resource allocation, facility location, or production planning." + }, + { + "name": "cuopt-lp-milp-api-c", + "source": "./skills/cuopt-lp-milp-api-c", + "skills": "./", + "description": "LP and MILP with cuOpt — C API only. Use when the user is embedding LP/MILP in C/C++." + }, + { + "name": "cuopt-lp-milp-api-cli", + "source": "./skills/cuopt-lp-milp-api-cli", + "skills": "./", + "description": "LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use when the user is solving from MPS via command line." + }, + { + "name": "routing-formulation", + "source": "./skills/routing-formulation", + "skills": "./", + "description": "Vehicle routing (VRP, TSP, PDP) — problem types and data requirements. Domain concepts; no API or interface." + }, + { + "name": "cuopt-routing-api-python", + "source": "./skills/cuopt-routing-api-python", + "skills": "./", + "description": "Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use when the user is building or solving routing in Python." + }, + { + "name": "qp-formulation", + "source": "./skills/qp-formulation", + "skills": "./", + "description": "Quadratic Programming (QP) — problem form and constraints. Domain concepts; no API or interface. QP is beta." + }, + { + "name": "cuopt-qp-api-python", + "source": "./skills/cuopt-qp-api-python", + "skills": "./", + "description": "Quadratic Programming (QP) with cuOpt — Python API only (beta). Use when the user is building or solving QP in Python." + }, + { + "name": "cuopt-qp-api-c", + "source": "./skills/cuopt-qp-api-c", + "skills": "./", + "description": "Quadratic Programming (QP) with cuOpt — C API. Use when the user is embedding QP in C/C++." + }, + { + "name": "cuopt-qp-api-cli", + "source": "./skills/cuopt-qp-api-cli", + "skills": "./", + "description": "QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use when the user is solving QP from the command line." + }, + { + "name": "cuopt-server-common", + "source": "./skills/cuopt-server-common", + "skills": "./", + "description": "cuOpt REST server — what it does and how requests flow. Domain concepts; no deploy or client code." + }, + { + "name": "cuopt-server-api-python", + "source": "./skills/cuopt-server-api-python", + "skills": "./", + "description": "cuOpt REST server — start server, endpoints, Python/curl client examples. Use when the user is deploying or calling the REST API." + } + ] +} diff --git a/.claude/AGENTS.md b/.claude/AGENTS.md index f5f4bd7b93..be77ac83a1 120000 --- a/.claude/AGENTS.md +++ b/.claude/AGENTS.md @@ -1 +1 @@ -../.github/AGENTS.md \ No newline at end of file +../AGENTS.md \ No newline at end of file diff --git a/.claude/skills b/.claude/skills index 3e73f3a383..42c5394a18 120000 --- a/.claude/skills +++ b/.claude/skills @@ -1 +1 @@ -../.github/skills \ No newline at end of file +../skills \ No newline at end of file diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json new file mode 100644 index 0000000000..efaa333059 --- /dev/null +++ b/.cursor-plugin/plugin.json @@ -0,0 +1,22 @@ +{ + "name": "nvidia-cuopt-skills", + "description": "Agent skills for NVIDIA cuOpt: routing (VRP, TSP, PDP), LP/MILP/QP, installation (Python/C/developer), and REST server. Use when building or solving optimization with cuOpt.", + "version": "1.0.0", + "author": { + "name": "NVIDIA" + }, + "homepage": "https://github.com/NVIDIA/cuopt", + "repository": "https://github.com/NVIDIA/cuopt", + "license": "Apache-2.0", + "skills": "skills", + "keywords": [ + "nvidia", + "cuopt", + "optimization", + "routing", + "vrp", + "lp", + "milp", + "qp" + ] +} diff --git a/.cursor/AGENTS.md b/.cursor/AGENTS.md index f5f4bd7b93..be77ac83a1 120000 --- a/.cursor/AGENTS.md +++ b/.cursor/AGENTS.md @@ -1 +1 @@ -../.github/AGENTS.md \ No newline at end of file +../AGENTS.md \ No newline at end of file diff --git a/.cursor/skills b/.cursor/skills index 3e73f3a383..42c5394a18 120000 --- a/.cursor/skills +++ b/.cursor/skills @@ -1 +1 @@ -../.github/skills \ No newline at end of file +../skills \ No newline at end of file diff --git a/.github/AGENTS.md b/.github/AGENTS.md deleted file mode 100644 index 42d971fd9d..0000000000 --- a/.github/AGENTS.md +++ /dev/null @@ -1,47 +0,0 @@ -# AGENTS.md - cuOpt AI Agent Entry Point - -AI agent skills for NVIDIA cuOpt optimization engine. Skills use a **flat layout**: **common** (concepts) + **api-python** or **api-c** (implementation) per domain. - -> **🔒 MANDATORY — Security:** You MUST NOT install, upgrade, or modify packages. Provide the exact command for the user to run; they execute it. No exceptions. - -> **🔒 MANDATORY — Ambiguity:** When the problem could be read more than one way, you MUST either **ask the user to clarify** or **solve every plausible interpretation and report all outcomes**. Never pick one interpretation silently. - -## Skills directory (flat) - -### Rules -- `skills/cuopt-user-rules/` — User-facing behavior and conventions; read first when helping users with cuOpt (routing, LP, MILP, QP, install, server). Choose skills from the index below by task, problem type, and interface (Python / C / CLI). -- `skills/cuopt-developer/` — Contributing and development; use when the user is building from source, contributing code, or working on cuOpt internals. - -### Common (concepts only; no API code) -- `skills/cuopt-installation-common/` — Install: system and environment requirements (concepts only; no install commands or interface) -- `skills/lp-milp-formulation/` — LP/MILP: concepts + problem parsing (parameters, constraints, decisions, objective) -- `skills/routing-formulation/` — Routing: VRP, TSP, PDP (problem types, data) -- `skills/qp-formulation/` — QP: minimize-only, escalate (beta) -- `skills/cuopt-server-common/` — Server: capabilities, workflow - -### API (implementation; one interface per skill) -- `skills/cuopt-installation-api-python/` -- `skills/cuopt-installation-api-c/` -- `skills/cuopt-installation-developer/` (build from source) -- `skills/cuopt-lp-milp-api-python/` -- `skills/cuopt-lp-milp-api-c/` -- `skills/cuopt-lp-milp-api-cli/` -- `skills/cuopt-routing-api-python/` -- `skills/cuopt-qp-api-python/` -- `skills/cuopt-qp-api-c/` -- `skills/cuopt-qp-api-cli/` -- `skills/cuopt-server-api-python/` (deploy + client) - -## Resources - -### Documentation -- [cuOpt User Guide](https://docs.nvidia.com/cuopt/user-guide/latest/introduction.html) -- [API Reference](https://docs.nvidia.com/cuopt/user-guide/latest/api.html) - -### Examples -- [cuopt-examples repo](https://github.com/NVIDIA/cuopt-examples) -- [Google Colab notebooks](https://colab.research.google.com/github/nvidia/cuopt-examples/) - -### Support -- [GitHub Issues](https://github.com/NVIDIA/cuopt/issues) -- [Developer Forums](https://forums.developer.nvidia.com/c/ai-data-science/nvidia-cuopt/514) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cf3a570486..7958eac440 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -22,7 +22,7 @@ container-builder/ @nvidia/cuopt-infra-codeowners /.github/.coderabbit_review_guide.md @nvidia/cuopt-infra-codeowners /.github/ISSUE_TEMPLATE/ @nvidia/cuopt-infra-codeowners /.github/PULL_REQUEST_TEMPLATE.md @nvidia/cuopt-infra-codeowners -/.github/skills/ @nvidia/cuopt-infra-codeowners +/skills/ @nvidia/cuopt-infra-codeowners /.github/agents-legacy/ @nvidia/cuopt-infra-codeowners #packaging code owners diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 4862fb890c..4d4a507843 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -118,7 +118,7 @@ jobs: - '**' - '!.ai/**' - '!.coderabbit.yaml' - - '!.github/AGENTS.md' + - '!AGENTS.md' - '!.github/CODE_OF_CONDUCT.md' - '!.github/CODEOWNERS' - '!.github/ISSUE_TEMPLATE/**' @@ -153,7 +153,7 @@ jobs: - '!README.md' - '!.ai/**' - '!.coderabbit.yaml' - - '!.github/AGENTS.md' + - '!AGENTS.md' - '!.github/CODE_OF_CONDUCT.md' - '!.github/CODEOWNERS' - '!.github/ISSUE_TEMPLATE/**' @@ -190,7 +190,7 @@ jobs: - '!README.md' - '!.ai/**' - '!.coderabbit.yaml' - - '!.github/AGENTS.md' + - '!AGENTS.md' - '!.github/CODE_OF_CONDUCT.md' - '!.github/CODEOWNERS' - '!.github/ISSUE_TEMPLATE/**' @@ -224,7 +224,7 @@ jobs: - '!README.md' - '!.ai/**' - '!.coderabbit.yaml' - - '!.github/AGENTS.md' + - '!AGENTS.md' - '!.github/CODE_OF_CONDUCT.md' - '!.github/CODEOWNERS' - '!.github/ISSUE_TEMPLATE/**' diff --git a/.opencode/AGENTS.md b/.opencode/AGENTS.md index f5f4bd7b93..be77ac83a1 120000 --- a/.opencode/AGENTS.md +++ b/.opencode/AGENTS.md @@ -1 +1 @@ -../.github/AGENTS.md \ No newline at end of file +../AGENTS.md \ No newline at end of file diff --git a/.opencode/skills b/.opencode/skills index 3e73f3a383..42c5394a18 120000 --- a/.opencode/skills +++ b/.opencode/skills @@ -1 +1 @@ -../.github/skills \ No newline at end of file +../skills \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2d968a4622..9c1d94a7dc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -103,6 +103,12 @@ repos: entry: python ci/utils/update_doc_versions.py language: system files: docs/cuopt/source/versions1.json + - id: validate-skills + name: Validate agent skills + entry: ci/utils/validate_skills.sh + language: system + pass_filenames: false + files: ^(skills/|\.claude-plugin/|\.cursor-plugin/|agents/|ci/utils/validate_skills\.sh|gemini-extension\.json)$ default_language_version: diff --git a/AGENTS.md b/AGENTS.md index f4e47cde2b..74d5aa66e6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,47 @@ -# AGENTS.md +# AGENTS.md — cuOpt AI Agent Entry Point -AI-agent skills for this repo are located at: +AI agent skills for NVIDIA cuOpt optimization engine. Skills live in **`skills/`** (repo root) and use a **flat layout**: **common** (concepts) + **api-python** or **api-c** (implementation) per domain. -- **Entry point**: `.github/AGENTS.md` -- **Skills**: `.github/skills/` +> **🔒 MANDATORY — Security:** You MUST NOT install, upgrade, or modify packages. Provide the exact command for the user to run; they execute it. No exceptions. -If you are a coding agent, start at `.github/AGENTS.md`. +> **🔒 MANDATORY — Ambiguity:** When the problem could be read more than one way, you MUST either **ask the user to clarify** or **solve every plausible interpretation and report all outcomes**. Never pick one interpretation silently. + +## Skills directory (flat) + +### Rules +- `skills/cuopt-user-rules/` — User-facing behavior and conventions; read first when helping users with cuOpt (routing, LP, MILP, QP, install, server). Choose skills from the index below by task, problem type, and interface (Python / C / CLI). +- `skills/cuopt-developer/` — Contributing and development; use when the user is building from source, contributing code, or working on cuOpt internals. + +### Common (concepts only; no API code) +- `skills/cuopt-installation-common/` — Install: system and environment requirements (concepts only; no install commands or interface) +- `skills/lp-milp-formulation/` — LP/MILP: concepts + problem parsing (parameters, constraints, decisions, objective) +- `skills/routing-formulation/` — Routing: VRP, TSP, PDP (problem types, data) +- `skills/qp-formulation/` — QP: minimize-only, escalate (beta) +- `skills/cuopt-server-common/` — Server: capabilities, workflow + +### API (implementation; one interface per skill) +- `skills/cuopt-installation-api-python/` +- `skills/cuopt-installation-api-c/` +- `skills/cuopt-installation-developer/` (build from source) +- `skills/cuopt-lp-milp-api-python/` +- `skills/cuopt-lp-milp-api-c/` +- `skills/cuopt-lp-milp-api-cli/` +- `skills/cuopt-routing-api-python/` +- `skills/cuopt-qp-api-python/` +- `skills/cuopt-qp-api-c/` +- `skills/cuopt-qp-api-cli/` +- `skills/cuopt-server-api-python/` (deploy + client) + +## Resources + +### Documentation +- [cuOpt User Guide](https://docs.nvidia.com/cuopt/user-guide/latest/introduction.html) +- [API Reference](https://docs.nvidia.com/cuopt/user-guide/latest/api.html) + +### Examples +- [cuopt-examples repo](https://github.com/NVIDIA/cuopt-examples) +- [Google Colab notebooks](https://colab.research.google.com/github/nvidia/cuopt-examples/) + +### Support +- [GitHub Issues](https://github.com/NVIDIA/cuopt/issues) +- [Developer Forums](https://forums.developer.nvidia.com/c/ai-data-science/nvidia-cuopt/514) diff --git a/agents/AGENTS.md b/agents/AGENTS.md new file mode 100644 index 0000000000..cf2598bc91 --- /dev/null +++ b/agents/AGENTS.md @@ -0,0 +1,59 @@ +# cuOpt Skills Reference + +You have additional skills documented in `skills//SKILL.md`. **When the user's intent matches a skill below, you MUST read that skill's SKILL.md** and follow its guidance. + +## Mandatory rules + +- **Security:** You MUST NOT install, upgrade, or modify packages. Provide the exact command for the user to run; they execute it. +- **Ambiguity:** When the problem could be read more than one way, either ask the user to clarify or solve every plausible interpretation and report all outcomes. Never pick one interpretation silently. + +## Available skills + +| Skill | Description | +|-------|-------------| +| cuopt-user-rules | Base behavior rules for using NVIDIA cuOpt. Read this FIRST before any cuOpt user task (routing, LP/MILP, QP, installation, server). | +| cuopt-developer | Contribute to NVIDIA cuOpt codebase (C++/CUDA, Python, server, docs, CI). Use when the user wants to modify solver internals, add features, submit PRs, or understand the codebase. | +| cuopt-installation-common | Install cuOpt — system and environment requirements only. Domain concepts; no install commands or interface. | +| cuopt-installation-api-python | Install cuOpt for Python — pip, conda, Docker, verification. Use when installing or verifying the Python API. | +| cuopt-installation-api-c | Install cuOpt for C — conda, locate lib/headers, verification. Use when installing or verifying the C API. | +| cuopt-installation-developer | Developer installation — build cuOpt from source, run tests. Use when setting up a dev environment to contribute or modify cuOpt. | +| lp-milp-formulation | LP/MILP concepts and going from problem text to formulation. Parameters, constraints, decisions, objective. | +| cuopt-lp-milp-api-python | Solve LP and MILP with the Python API. Use for linear constraints, integer variables, scheduling, resource allocation, facility location, production planning. | +| cuopt-lp-milp-api-c | LP and MILP with cuOpt — C API. Use when embedding LP/MILP in C/C++. | +| cuopt-lp-milp-api-cli | LP and MILP with cuOpt — CLI (MPS files, cuopt_cli). Use when solving from MPS via command line. | +| routing-formulation | Vehicle routing (VRP, TSP, PDP) — problem types and data requirements. Domain concepts only. | +| cuopt-routing-api-python | Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API. Use when building or solving routing in Python. | +| qp-formulation | Quadratic Programming (QP) — problem form and constraints. Domain concepts; QP is beta. | +| cuopt-qp-api-python | QP with cuOpt — Python API (beta). Use when building or solving QP in Python. | +| cuopt-qp-api-c | QP with cuOpt — C API. Use when embedding QP in C/C++. | +| cuopt-qp-api-cli | QP with cuOpt — CLI. Use when solving QP from the command line. | +| cuopt-server-common | cuOpt REST server — what it does and how requests flow. Domain concepts only. | +| cuopt-server-api-python | cuOpt REST server — start server, endpoints, Python/curl client examples. Use when deploying or calling the REST API. | + +## Skill paths (from repo root) + +- `skills/cuopt-user-rules/SKILL.md` +- `skills/cuopt-developer/SKILL.md` +- `skills/cuopt-installation-common/SKILL.md` +- `skills/cuopt-installation-api-python/SKILL.md` +- `skills/cuopt-installation-api-c/SKILL.md` +- `skills/cuopt-installation-developer/SKILL.md` +- `skills/lp-milp-formulation/SKILL.md` +- `skills/cuopt-lp-milp-api-python/SKILL.md` +- `skills/cuopt-lp-milp-api-c/SKILL.md` +- `skills/cuopt-lp-milp-api-cli/SKILL.md` +- `skills/routing-formulation/SKILL.md` +- `skills/cuopt-routing-api-python/SKILL.md` +- `skills/qp-formulation/SKILL.md` +- `skills/cuopt-qp-api-python/SKILL.md` +- `skills/cuopt-qp-api-c/SKILL.md` +- `skills/cuopt-qp-api-cli/SKILL.md` +- `skills/cuopt-server-common/SKILL.md` +- `skills/cuopt-server-api-python/SKILL.md` + +## Resources + +- [cuOpt User Guide](https://docs.nvidia.com/cuopt/user-guide/latest/introduction.html) +- [API Reference](https://docs.nvidia.com/cuopt/user-guide/latest/api.html) +- [cuopt-examples](https://github.com/NVIDIA/cuopt-examples) +- [GitHub Issues](https://github.com/NVIDIA/cuopt/issues) diff --git a/ci/test_python.sh b/ci/test_python.sh index 1e703c750a..4f91c83334 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -74,7 +74,7 @@ timeout 20m ./ci/run_cuopt_server_pytests.sh \ --cov-report=xml:"${RAPIDS_COVERAGE_DIR}/cuopt-server-coverage.xml" \ --cov-report=term -rapids-logger "Test .github/skills assets (Python, C, CLI)" +rapids-logger "Test skills/ assets (Python, C, CLI)" timeout 10m ./ci/test_skills_assets.sh rapids-logger "Test script exiting with value: $EXITCODE" diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh index 7cb39ff8a1..f40d4c44a7 100755 --- a/ci/test_skills_assets.sh +++ b/ci/test_skills_assets.sh @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -# Run all assets under .github/skills (Python, C, CLI) as part of conda Python test. +# Run all assets under skills/ (Python, C, CLI) as part of conda Python test. # Python: run each .py from its directory (server API clients need server on port 8000). # C: compile and run each .c with libcuopt. # CLI: run cuopt_cli on each sample .mps in API-CLI skill assets. @@ -18,12 +18,12 @@ else fi REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" -SKILLS_ASSETS="${REPO_ROOT}/.github/skills" +SKILLS_ASSETS="${REPO_ROOT}/skills" FAILED=() SERVER_PID="" if [[ ! -d "${SKILLS_ASSETS}" ]]; then - log "No .github/skills directory found, skipping skills asset tests" + log "No skills directory found, skipping skills asset tests" exit 0 fi @@ -58,7 +58,7 @@ trap stop_server EXIT start_server # ---- Python assets ---- -log "Testing Python assets in .github/skills" +log "Testing Python assets in skills/" while IFS= read -r -d '' script; do dir=$(dirname "$script") name=$(basename "$script") @@ -78,7 +78,7 @@ if [[ -n "${CONDA_PREFIX:-}" ]]; then LIB_PATH="${CONDA_PREFIX}/lib" export LD_LIBRARY_PATH="${LIB_PATH}:${LD_LIBRARY_PATH:-}" - log "Testing C assets in .github/skills" + log "Testing C assets in skills/" while IFS= read -r -d '' cfile; do dir=$(dirname "$cfile") base=$(basename "$cfile" .c) @@ -106,7 +106,7 @@ else fi # ---- CLI assets (cuopt_cli with sample MPS files) ---- -log "Testing CLI assets in .github/skills" +log "Testing CLI assets in skills/" while IFS= read -r -d '' mps; do rel="${mps#"$REPO_ROOT/"}" log "Running CLI asset: $rel" diff --git a/ci/utils/validate_skills.sh b/ci/utils/validate_skills.sh new file mode 100644 index 0000000000..0b103f1926 --- /dev/null +++ b/ci/utils/validate_skills.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Validate cuOpt agent skills and plugin manifests. +# Run from repo root: ./ci/utils/validate_skills.sh +set -e + +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$REPO_ROOT" + +SKILLS_DIR="skills" +CLAUDE_MARKETPLACE=".claude-plugin/marketplace.json" +AGENTS_MD="agents/AGENTS.md" +ERRORS=0 + +echo "Validating skills in $SKILLS_DIR..." + +for dir in "$SKILLS_DIR"/*/; do + [ -d "$dir" ] || continue + name=$(basename "$dir") + skill_md="${dir}SKILL.md" + if [ ! -f "$skill_md" ]; then + echo "ERROR: $name missing SKILL.md" + ERRORS=$((ERRORS + 1)) + continue + fi + if ! grep -q '^name:' "$skill_md" || ! grep -q '^description:' "$skill_md"; then + echo "ERROR: $name/SKILL.md missing frontmatter (name: or description:)" + ERRORS=$((ERRORS + 1)) + fi +done + +if [ -f "$CLAUDE_MARKETPLACE" ]; then + echo "Validating $CLAUDE_MARKETPLACE..." + while IFS= read -r line; do + path=$(echo "$line" | sed -n 's/.*"source"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') + [ -z "$path" ] && continue + path="${path#./}" + if [ -n "$path" ] && [ ! -f "$path/SKILL.md" ]; then + echo "ERROR: marketplace.json source missing SKILL.md: $path" + ERRORS=$((ERRORS + 1)) + fi + done < <(grep '"source"' "$CLAUDE_MARKETPLACE" || true) + for dir in "$SKILLS_DIR"/*/; do + [ -d "$dir" ] || continue + name=$(basename "$dir") + if ! grep -q "\"name\": \"$name\"" "$CLAUDE_MARKETPLACE"; then + echo "ERROR: skill $name not listed in $CLAUDE_MARKETPLACE" + ERRORS=$((ERRORS + 1)) + fi + done +fi + +if [ -f "$AGENTS_MD" ]; then + echo "Validating $AGENTS_MD references..." + for dir in "$SKILLS_DIR"/*/; do + [ -d "$dir" ] || continue + name=$(basename "$dir") + if ! grep -q "$name" "$AGENTS_MD"; then + echo "ERROR: agents/AGENTS.md does not reference skill: $name" + ERRORS=$((ERRORS + 1)) + fi + done +fi + +if [ $ERRORS -gt 0 ]; then + echo "Validation failed with $ERRORS error(s)." + exit 1 +fi +echo "All validations passed." diff --git a/gemini-extension.json b/gemini-extension.json new file mode 100644 index 0000000000..ace2067bcb --- /dev/null +++ b/gemini-extension.json @@ -0,0 +1,6 @@ +{ + "name": "nvidia-cuopt-skills", + "description": "Agent skills for NVIDIA cuOpt optimization engine: routing, LP/MILP/QP, installation, and server.", + "version": "1.0.0", + "contextFileName": "AGENTS.md" +} diff --git a/.github/skills/cuopt-developer/SKILL.md b/skills/cuopt-developer/SKILL.md similarity index 100% rename from .github/skills/cuopt-developer/SKILL.md rename to skills/cuopt-developer/SKILL.md diff --git a/.github/skills/cuopt-installation-api-c/SKILL.md b/skills/cuopt-installation-api-c/SKILL.md similarity index 100% rename from .github/skills/cuopt-installation-api-c/SKILL.md rename to skills/cuopt-installation-api-c/SKILL.md diff --git a/.github/skills/cuopt-installation-api-c/resources/verification_examples.md b/skills/cuopt-installation-api-c/resources/verification_examples.md similarity index 100% rename from .github/skills/cuopt-installation-api-c/resources/verification_examples.md rename to skills/cuopt-installation-api-c/resources/verification_examples.md diff --git a/.github/skills/cuopt-installation-api-python/SKILL.md b/skills/cuopt-installation-api-python/SKILL.md similarity index 100% rename from .github/skills/cuopt-installation-api-python/SKILL.md rename to skills/cuopt-installation-api-python/SKILL.md diff --git a/.github/skills/cuopt-installation-api-python/resources/verification_examples.md b/skills/cuopt-installation-api-python/resources/verification_examples.md similarity index 100% rename from .github/skills/cuopt-installation-api-python/resources/verification_examples.md rename to skills/cuopt-installation-api-python/resources/verification_examples.md diff --git a/.github/skills/cuopt-installation-common/SKILL.md b/skills/cuopt-installation-common/SKILL.md similarity index 100% rename from .github/skills/cuopt-installation-common/SKILL.md rename to skills/cuopt-installation-common/SKILL.md diff --git a/.github/skills/cuopt-installation-developer/SKILL.md b/skills/cuopt-installation-developer/SKILL.md similarity index 100% rename from .github/skills/cuopt-installation-developer/SKILL.md rename to skills/cuopt-installation-developer/SKILL.md diff --git a/.github/skills/cuopt-lp-milp-api-c/SKILL.md b/skills/cuopt-lp-milp-api-c/SKILL.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/SKILL.md rename to skills/cuopt-lp-milp-api-c/SKILL.md diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/README.md b/skills/cuopt-lp-milp-api-c/assets/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/README.md rename to skills/cuopt-lp-milp-api-c/assets/README.md diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md b/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md rename to skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c b/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c rename to skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md b/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md rename to skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c b/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c rename to skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md b/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md rename to skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md b/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md rename to skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c b/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c rename to skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md b/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md rename to skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c b/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c rename to skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md b/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md rename to skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps b/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps rename to skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps diff --git a/.github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c b/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c rename to skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c diff --git a/.github/skills/cuopt-lp-milp-api-c/resources/examples.md b/skills/cuopt-lp-milp-api-c/resources/examples.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-c/resources/examples.md rename to skills/cuopt-lp-milp-api-c/resources/examples.md diff --git a/.github/skills/cuopt-lp-milp-api-cli/SKILL.md b/skills/cuopt-lp-milp-api-cli/SKILL.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-cli/SKILL.md rename to skills/cuopt-lp-milp-api-cli/SKILL.md diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/README.md b/skills/cuopt-lp-milp-api-cli/assets/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-cli/assets/README.md rename to skills/cuopt-lp-milp-api-cli/assets/README.md diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md b/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md rename to skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps b/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps similarity index 100% rename from .github/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps rename to skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md b/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md rename to skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps b/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps similarity index 100% rename from .github/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps rename to skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md b/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md rename to skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md diff --git a/.github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps b/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps similarity index 100% rename from .github/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps rename to skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps diff --git a/.github/skills/cuopt-lp-milp-api-python/SKILL.md b/skills/cuopt-lp-milp-api-python/SKILL.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/SKILL.md rename to skills/cuopt-lp-milp-api-python/SKILL.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/README.md b/skills/cuopt-lp-milp-api-python/assets/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/README.md rename to skills/cuopt-lp-milp-api-python/assets/README.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md b/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md rename to skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py b/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py rename to skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md b/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md rename to skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py b/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py rename to skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md b/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md rename to skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py b/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py rename to skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md b/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md rename to skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py b/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py rename to skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py b/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py rename to skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md b/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md rename to skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py b/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py rename to skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md b/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md rename to skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md b/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md rename to skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps b/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps rename to skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py b/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py rename to skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py diff --git a/.github/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md b/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md similarity index 100% rename from .github/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md rename to skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md diff --git a/.github/skills/cuopt-qp-api-c/SKILL.md b/skills/cuopt-qp-api-c/SKILL.md similarity index 100% rename from .github/skills/cuopt-qp-api-c/SKILL.md rename to skills/cuopt-qp-api-c/SKILL.md diff --git a/.github/skills/cuopt-qp-api-c/assets/README.md b/skills/cuopt-qp-api-c/assets/README.md similarity index 100% rename from .github/skills/cuopt-qp-api-c/assets/README.md rename to skills/cuopt-qp-api-c/assets/README.md diff --git a/.github/skills/cuopt-qp-api-cli/SKILL.md b/skills/cuopt-qp-api-cli/SKILL.md similarity index 100% rename from .github/skills/cuopt-qp-api-cli/SKILL.md rename to skills/cuopt-qp-api-cli/SKILL.md diff --git a/.github/skills/cuopt-qp-api-cli/assets/README.md b/skills/cuopt-qp-api-cli/assets/README.md similarity index 100% rename from .github/skills/cuopt-qp-api-cli/assets/README.md rename to skills/cuopt-qp-api-cli/assets/README.md diff --git a/.github/skills/cuopt-qp-api-python/SKILL.md b/skills/cuopt-qp-api-python/SKILL.md similarity index 100% rename from .github/skills/cuopt-qp-api-python/SKILL.md rename to skills/cuopt-qp-api-python/SKILL.md diff --git a/.github/skills/cuopt-qp-api-python/assets/README.md b/skills/cuopt-qp-api-python/assets/README.md similarity index 100% rename from .github/skills/cuopt-qp-api-python/assets/README.md rename to skills/cuopt-qp-api-python/assets/README.md diff --git a/.github/skills/cuopt-qp-api-python/assets/least_squares/README.md b/skills/cuopt-qp-api-python/assets/least_squares/README.md similarity index 100% rename from .github/skills/cuopt-qp-api-python/assets/least_squares/README.md rename to skills/cuopt-qp-api-python/assets/least_squares/README.md diff --git a/.github/skills/cuopt-qp-api-python/assets/least_squares/model.py b/skills/cuopt-qp-api-python/assets/least_squares/model.py similarity index 100% rename from .github/skills/cuopt-qp-api-python/assets/least_squares/model.py rename to skills/cuopt-qp-api-python/assets/least_squares/model.py diff --git a/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md b/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md similarity index 100% rename from .github/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md rename to skills/cuopt-qp-api-python/assets/maximization_workaround/README.md diff --git a/.github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py b/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py similarity index 100% rename from .github/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py rename to skills/cuopt-qp-api-python/assets/maximization_workaround/model.py diff --git a/.github/skills/cuopt-qp-api-python/assets/portfolio/README.md b/skills/cuopt-qp-api-python/assets/portfolio/README.md similarity index 100% rename from .github/skills/cuopt-qp-api-python/assets/portfolio/README.md rename to skills/cuopt-qp-api-python/assets/portfolio/README.md diff --git a/.github/skills/cuopt-qp-api-python/assets/portfolio/model.py b/skills/cuopt-qp-api-python/assets/portfolio/model.py similarity index 100% rename from .github/skills/cuopt-qp-api-python/assets/portfolio/model.py rename to skills/cuopt-qp-api-python/assets/portfolio/model.py diff --git a/.github/skills/cuopt-qp-api-python/resources/examples.md b/skills/cuopt-qp-api-python/resources/examples.md similarity index 100% rename from .github/skills/cuopt-qp-api-python/resources/examples.md rename to skills/cuopt-qp-api-python/resources/examples.md diff --git a/.github/skills/cuopt-routing-api-python/SKILL.md b/skills/cuopt-routing-api-python/SKILL.md similarity index 100% rename from .github/skills/cuopt-routing-api-python/SKILL.md rename to skills/cuopt-routing-api-python/SKILL.md diff --git a/.github/skills/cuopt-routing-api-python/assets/README.md b/skills/cuopt-routing-api-python/assets/README.md similarity index 100% rename from .github/skills/cuopt-routing-api-python/assets/README.md rename to skills/cuopt-routing-api-python/assets/README.md diff --git a/.github/skills/cuopt-routing-api-python/assets/pdp_basic/README.md b/skills/cuopt-routing-api-python/assets/pdp_basic/README.md similarity index 100% rename from .github/skills/cuopt-routing-api-python/assets/pdp_basic/README.md rename to skills/cuopt-routing-api-python/assets/pdp_basic/README.md diff --git a/.github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py b/skills/cuopt-routing-api-python/assets/pdp_basic/model.py similarity index 100% rename from .github/skills/cuopt-routing-api-python/assets/pdp_basic/model.py rename to skills/cuopt-routing-api-python/assets/pdp_basic/model.py diff --git a/.github/skills/cuopt-routing-api-python/assets/vrp_basic/README.md b/skills/cuopt-routing-api-python/assets/vrp_basic/README.md similarity index 100% rename from .github/skills/cuopt-routing-api-python/assets/vrp_basic/README.md rename to skills/cuopt-routing-api-python/assets/vrp_basic/README.md diff --git a/.github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py b/skills/cuopt-routing-api-python/assets/vrp_basic/model.py similarity index 100% rename from .github/skills/cuopt-routing-api-python/assets/vrp_basic/model.py rename to skills/cuopt-routing-api-python/assets/vrp_basic/model.py diff --git a/.github/skills/cuopt-routing-api-python/resources/examples.md b/skills/cuopt-routing-api-python/resources/examples.md similarity index 100% rename from .github/skills/cuopt-routing-api-python/resources/examples.md rename to skills/cuopt-routing-api-python/resources/examples.md diff --git a/.github/skills/cuopt-routing-api-python/resources/server_examples.md b/skills/cuopt-routing-api-python/resources/server_examples.md similarity index 100% rename from .github/skills/cuopt-routing-api-python/resources/server_examples.md rename to skills/cuopt-routing-api-python/resources/server_examples.md diff --git a/.github/skills/cuopt-server-api-python/SKILL.md b/skills/cuopt-server-api-python/SKILL.md similarity index 100% rename from .github/skills/cuopt-server-api-python/SKILL.md rename to skills/cuopt-server-api-python/SKILL.md diff --git a/.github/skills/cuopt-server-api-python/assets/README.md b/skills/cuopt-server-api-python/assets/README.md similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/README.md rename to skills/cuopt-server-api-python/assets/README.md diff --git a/.github/skills/cuopt-server-api-python/assets/lp_basic/README.md b/skills/cuopt-server-api-python/assets/lp_basic/README.md similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/lp_basic/README.md rename to skills/cuopt-server-api-python/assets/lp_basic/README.md diff --git a/.github/skills/cuopt-server-api-python/assets/lp_basic/client.py b/skills/cuopt-server-api-python/assets/lp_basic/client.py similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/lp_basic/client.py rename to skills/cuopt-server-api-python/assets/lp_basic/client.py diff --git a/.github/skills/cuopt-server-api-python/assets/milp_basic/README.md b/skills/cuopt-server-api-python/assets/milp_basic/README.md similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/milp_basic/README.md rename to skills/cuopt-server-api-python/assets/milp_basic/README.md diff --git a/.github/skills/cuopt-server-api-python/assets/milp_basic/client.py b/skills/cuopt-server-api-python/assets/milp_basic/client.py similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/milp_basic/client.py rename to skills/cuopt-server-api-python/assets/milp_basic/client.py diff --git a/.github/skills/cuopt-server-api-python/assets/pdp_basic/README.md b/skills/cuopt-server-api-python/assets/pdp_basic/README.md similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/pdp_basic/README.md rename to skills/cuopt-server-api-python/assets/pdp_basic/README.md diff --git a/.github/skills/cuopt-server-api-python/assets/pdp_basic/client.py b/skills/cuopt-server-api-python/assets/pdp_basic/client.py similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/pdp_basic/client.py rename to skills/cuopt-server-api-python/assets/pdp_basic/client.py diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_basic/README.md b/skills/cuopt-server-api-python/assets/vrp_basic/README.md similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/vrp_basic/README.md rename to skills/cuopt-server-api-python/assets/vrp_basic/README.md diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_basic/client.py b/skills/cuopt-server-api-python/assets/vrp_basic/client.py similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/vrp_basic/client.py rename to skills/cuopt-server-api-python/assets/vrp_basic/client.py diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_simple/README.md b/skills/cuopt-server-api-python/assets/vrp_simple/README.md similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/vrp_simple/README.md rename to skills/cuopt-server-api-python/assets/vrp_simple/README.md diff --git a/.github/skills/cuopt-server-api-python/assets/vrp_simple/client.py b/skills/cuopt-server-api-python/assets/vrp_simple/client.py similarity index 100% rename from .github/skills/cuopt-server-api-python/assets/vrp_simple/client.py rename to skills/cuopt-server-api-python/assets/vrp_simple/client.py diff --git a/.github/skills/cuopt-server-common/SKILL.md b/skills/cuopt-server-common/SKILL.md similarity index 100% rename from .github/skills/cuopt-server-common/SKILL.md rename to skills/cuopt-server-common/SKILL.md diff --git a/.github/skills/cuopt-user-rules/SKILL.md b/skills/cuopt-user-rules/SKILL.md similarity index 100% rename from .github/skills/cuopt-user-rules/SKILL.md rename to skills/cuopt-user-rules/SKILL.md diff --git a/.github/skills/lp-milp-formulation/SKILL.md b/skills/lp-milp-formulation/SKILL.md similarity index 100% rename from .github/skills/lp-milp-formulation/SKILL.md rename to skills/lp-milp-formulation/SKILL.md diff --git a/.github/skills/qp-formulation/SKILL.md b/skills/qp-formulation/SKILL.md similarity index 100% rename from .github/skills/qp-formulation/SKILL.md rename to skills/qp-formulation/SKILL.md diff --git a/.github/skills/routing-formulation/SKILL.md b/skills/routing-formulation/SKILL.md similarity index 100% rename from .github/skills/routing-formulation/SKILL.md rename to skills/routing-formulation/SKILL.md From 7fce6dbfa0d15f90dd7d24c2a5f45b15534ca1e0 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 11:48:53 -0600 Subject: [PATCH 29/35] address review comment --- ci/utils/validate_skills.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/utils/validate_skills.sh b/ci/utils/validate_skills.sh index 0b103f1926..edc172e8a8 100644 --- a/ci/utils/validate_skills.sh +++ b/ci/utils/validate_skills.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + # Validate cuOpt agent skills and plugin manifests. # Run from repo root: ./ci/utils/validate_skills.sh set -e From 38fcfecad8ad751f605b26f4c21d8cc263da02b2 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 11:49:51 -0600 Subject: [PATCH 30/35] run validate --- ci/utils/validate_skills.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ci/utils/validate_skills.sh diff --git a/ci/utils/validate_skills.sh b/ci/utils/validate_skills.sh old mode 100644 new mode 100755 From a27a73a3bcec9284fa3de7204b8c100fc0bc4d64 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 20:26:45 -0600 Subject: [PATCH 31/35] Add version to skills --- .claude-plugin/marketplace.json | 2 +- .cursor-plugin/plugin.json | 2 +- .pre-commit-config.yaml | 8 ++- ci/utils/sync_skills_version.sh | 52 +++++++++++++++++++ ci/utils/validate_skills.sh | 28 ++++++++++ gemini-extension.json | 2 +- skills/cuopt-developer/SKILL.md | 1 + skills/cuopt-installation-api-c/SKILL.md | 1 + skills/cuopt-installation-api-python/SKILL.md | 1 + skills/cuopt-installation-common/SKILL.md | 1 + skills/cuopt-installation-developer/SKILL.md | 1 + skills/cuopt-lp-milp-api-c/SKILL.md | 1 + skills/cuopt-lp-milp-api-cli/SKILL.md | 1 + skills/cuopt-lp-milp-api-python/SKILL.md | 1 + skills/cuopt-qp-api-c/SKILL.md | 1 + skills/cuopt-qp-api-cli/SKILL.md | 1 + skills/cuopt-qp-api-python/SKILL.md | 1 + skills/cuopt-routing-api-python/SKILL.md | 1 + skills/cuopt-server-api-python/SKILL.md | 1 + skills/cuopt-server-common/SKILL.md | 1 + skills/cuopt-user-rules/SKILL.md | 1 + skills/lp-milp-formulation/SKILL.md | 1 + skills/qp-formulation/SKILL.md | 1 + skills/routing-formulation/SKILL.md | 1 + 24 files changed, 108 insertions(+), 4 deletions(-) create mode 100755 ci/utils/sync_skills_version.sh diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 45172e5a19..c1a01b7d42 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -5,7 +5,7 @@ }, "metadata": { "description": "Agent skills for NVIDIA cuOpt: routing (VRP, TSP, PDP), LP/MILP/QP, installation (Python/C/developer), and REST server.", - "version": "1.0.0" + "version": "26.04.00" }, "plugins": [ { diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index efaa333059..5f34873671 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "nvidia-cuopt-skills", "description": "Agent skills for NVIDIA cuOpt: routing (VRP, TSP, PDP), LP/MILP/QP, installation (Python/C/developer), and REST server. Use when building or solving optimization with cuOpt.", - "version": "1.0.0", + "version": "26.04.00", "author": { "name": "NVIDIA" }, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c1d94a7dc..93688aa567 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -103,12 +103,18 @@ repos: entry: python ci/utils/update_doc_versions.py language: system files: docs/cuopt/source/versions1.json + - id: sync-skills-version + name: Sync skills version from VERSION + entry: ci/utils/sync_skills_version.sh + language: system + pass_filenames: false + files: ^(VERSION|\.claude-plugin/marketplace\.json|\.cursor-plugin/plugin\.json|gemini-extension\.json)$ - id: validate-skills name: Validate agent skills entry: ci/utils/validate_skills.sh language: system pass_filenames: false - files: ^(skills/|\.claude-plugin/|\.cursor-plugin/|agents/|ci/utils/validate_skills\.sh|gemini-extension\.json)$ + files: ^(VERSION|skills/|\.claude-plugin/|\.cursor-plugin/|agents/|ci/utils/validate_skills\.sh|ci/utils/sync_skills_version\.sh|gemini-extension\.json)$ default_language_version: diff --git a/ci/utils/sync_skills_version.sh b/ci/utils/sync_skills_version.sh new file mode 100755 index 0000000000..1dfca8a663 --- /dev/null +++ b/ci/utils/sync_skills_version.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Sync skills/plugin version from repo root VERSION file. +# Run from repo root: ./ci/utils/sync_skills_version.sh +set -e + +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$REPO_ROOT" + +VERSION_FILE="${REPO_ROOT}/VERSION" +if [[ ! -f "${VERSION_FILE}" ]]; then + echo "ERROR: VERSION file not found at ${VERSION_FILE}" + exit 1 +fi + +RELEASE_VERSION=$(tr -d ' \n\r' < "${VERSION_FILE}") +if [[ -z "${RELEASE_VERSION}" ]]; then + echo "ERROR: VERSION file is empty" + exit 1 +fi + +echo "Syncing skills version to ${RELEASE_VERSION} (from VERSION)..." + +# .cursor-plugin/plugin.json and gemini-extension.json: top-level "version" +for f in .cursor-plugin/plugin.json gemini-extension.json; do + if [[ -f "$f" ]]; then + sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${RELEASE_VERSION}\"/" "$f" + echo " updated $f" + fi +done + +# .claude-plugin/marketplace.json: metadata.version +if [[ -f ".claude-plugin/marketplace.json" ]]; then + sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${RELEASE_VERSION}\"/" .claude-plugin/marketplace.json + echo " updated .claude-plugin/marketplace.json" +fi + +# skills/*/SKILL.md: add or update version in YAML frontmatter (after name:) +SKILLS_DIR="skills" +for skill_md in "${SKILLS_DIR}"/*/SKILL.md; do + [[ -f "$skill_md" ]] || continue + if grep -q '^version:' "$skill_md" 2>/dev/null; then + sed -i "s/^version:.*/version: \"${RELEASE_VERSION}\"/" "$skill_md" + else + sed -i "/^name:/a version: \"${RELEASE_VERSION}\"" "$skill_md" + fi + echo " updated $skill_md" +done + +echo "Done. Skills version is now ${RELEASE_VERSION}." diff --git a/ci/utils/validate_skills.sh b/ci/utils/validate_skills.sh index edc172e8a8..6577a45a1e 100755 --- a/ci/utils/validate_skills.sh +++ b/ci/utils/validate_skills.sh @@ -12,8 +12,23 @@ cd "$REPO_ROOT" SKILLS_DIR="skills" CLAUDE_MARKETPLACE=".claude-plugin/marketplace.json" AGENTS_MD="agents/AGENTS.md" +VERSION_FILE="VERSION" ERRORS=0 +# Check skills version matches release version (VERSION file) +if [[ -f "${VERSION_FILE}" ]]; then + RELEASE_VERSION=$(tr -d ' \n\r' < "${VERSION_FILE}") + for f in .cursor-plugin/plugin.json gemini-extension.json .claude-plugin/marketplace.json; do + if [[ -f "$f" ]]; then + FILE_VERSION=$(grep '"version"' "$f" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + if [[ "${FILE_VERSION}" != "${RELEASE_VERSION}" ]]; then + echo "ERROR: $f has version \"${FILE_VERSION}\" but VERSION file has \"${RELEASE_VERSION}\". Run: ./ci/utils/sync_skills_version.sh" + ERRORS=$((ERRORS + 1)) + fi + fi + done +fi + echo "Validating skills in $SKILLS_DIR..." for dir in "$SKILLS_DIR"/*/; do @@ -29,6 +44,19 @@ for dir in "$SKILLS_DIR"/*/; do echo "ERROR: $name/SKILL.md missing frontmatter (name: or description:)" ERRORS=$((ERRORS + 1)) fi + if [[ -f "${VERSION_FILE}" ]]; then + RELEASE_VERSION=$(tr -d ' \n\r' < "${VERSION_FILE}") + if grep -q '^version:' "$skill_md" 2>/dev/null; then + SKILL_VERSION=$(sed -n 's/^version:[^0-9]*\([0-9][0-9.]*\).*/\1/p' "$skill_md" | head -1) + if [[ "${SKILL_VERSION}" != "${RELEASE_VERSION}" ]]; then + echo "ERROR: $name/SKILL.md has version \"${SKILL_VERSION}\" but VERSION file has \"${RELEASE_VERSION}\". Run: ./ci/utils/sync_skills_version.sh" + ERRORS=$((ERRORS + 1)) + fi + else + echo "ERROR: $name/SKILL.md missing version in frontmatter. Run: ./ci/utils/sync_skills_version.sh" + ERRORS=$((ERRORS + 1)) + fi + fi done if [ -f "$CLAUDE_MARKETPLACE" ]; then diff --git a/gemini-extension.json b/gemini-extension.json index ace2067bcb..b4c6b764a4 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,6 +1,6 @@ { "name": "nvidia-cuopt-skills", "description": "Agent skills for NVIDIA cuOpt optimization engine: routing, LP/MILP/QP, installation, and server.", - "version": "1.0.0", + "version": "26.04.00", "contextFileName": "AGENTS.md" } diff --git a/skills/cuopt-developer/SKILL.md b/skills/cuopt-developer/SKILL.md index 8e73995b58..12419153ac 100644 --- a/skills/cuopt-developer/SKILL.md +++ b/skills/cuopt-developer/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-developer +version: "26.04.00" description: Contribute to NVIDIA cuOpt codebase including C++/CUDA, Python, server, docs, and CI. Use when the user wants to modify solver internals, add features, submit PRs, or understand the codebase architecture. --- diff --git a/skills/cuopt-installation-api-c/SKILL.md b/skills/cuopt-installation-api-c/SKILL.md index 677ffac668..747382e3c7 100644 --- a/skills/cuopt-installation-api-c/SKILL.md +++ b/skills/cuopt-installation-api-c/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-installation-api-c +version: "26.04.00" description: Install cuOpt for C — conda, locate lib/headers, verification. Use when the user is installing or verifying the C API. Standalone; no common skill. --- diff --git a/skills/cuopt-installation-api-python/SKILL.md b/skills/cuopt-installation-api-python/SKILL.md index fa97fef33a..a3d7a5e5d2 100644 --- a/skills/cuopt-installation-api-python/SKILL.md +++ b/skills/cuopt-installation-api-python/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-installation-api-python +version: "26.04.00" description: Install cuOpt for Python — pip, conda, Docker, verification. Use when the user is installing or verifying the Python API. Standalone; no common skill. --- diff --git a/skills/cuopt-installation-common/SKILL.md b/skills/cuopt-installation-common/SKILL.md index 4f6bc4e973..6ceb9f9000 100644 --- a/skills/cuopt-installation-common/SKILL.md +++ b/skills/cuopt-installation-common/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-installation-common +version: "26.04.00" description: Install cuOpt — system and environment requirements only. Domain concepts; no install commands or interface guidance. --- diff --git a/skills/cuopt-installation-developer/SKILL.md b/skills/cuopt-installation-developer/SKILL.md index 471d67b300..a002498853 100644 --- a/skills/cuopt-installation-developer/SKILL.md +++ b/skills/cuopt-installation-developer/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-installation-developer +version: "26.04.00" description: Developer installation — build cuOpt from source, run tests. Use when the user wants to set up a dev environment to contribute or modify cuOpt. --- diff --git a/skills/cuopt-lp-milp-api-c/SKILL.md b/skills/cuopt-lp-milp-api-c/SKILL.md index 5bca8e7274..53df3de63e 100644 --- a/skills/cuopt-lp-milp-api-c/SKILL.md +++ b/skills/cuopt-lp-milp-api-c/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-lp-milp-api-c +version: "26.04.00" description: LP and MILP with cuOpt — C API only. Use when the user is embedding LP/MILP in C/C++. --- diff --git a/skills/cuopt-lp-milp-api-cli/SKILL.md b/skills/cuopt-lp-milp-api-cli/SKILL.md index dfb3ab8b50..cbdc1e7778 100644 --- a/skills/cuopt-lp-milp-api-cli/SKILL.md +++ b/skills/cuopt-lp-milp-api-cli/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-lp-milp-api-cli +version: "26.04.00" description: LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use when the user is solving from MPS via command line. --- diff --git a/skills/cuopt-lp-milp-api-python/SKILL.md b/skills/cuopt-lp-milp-api-python/SKILL.md index 272c2c5423..a7cd9a59f2 100644 --- a/skills/cuopt-lp-milp-api-python/SKILL.md +++ b/skills/cuopt-lp-milp-api-python/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-lp-milp-api-python +version: "26.04.00" description: Solve Linear Programming (LP) and Mixed-Integer Linear Programming (MILP) with the Python API. Use when the user asks about optimization with linear constraints, integer variables, scheduling, resource allocation, facility location, or production planning. --- diff --git a/skills/cuopt-qp-api-c/SKILL.md b/skills/cuopt-qp-api-c/SKILL.md index 77a62069d4..bc1efb63d3 100644 --- a/skills/cuopt-qp-api-c/SKILL.md +++ b/skills/cuopt-qp-api-c/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-qp-api-c +version: "26.04.00" description: Quadratic Programming (QP) with cuOpt — C API. Use when the user is embedding QP in C/C++. --- diff --git a/skills/cuopt-qp-api-cli/SKILL.md b/skills/cuopt-qp-api-cli/SKILL.md index 6af0cce6b4..5f8a8e848a 100644 --- a/skills/cuopt-qp-api-cli/SKILL.md +++ b/skills/cuopt-qp-api-cli/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-qp-api-cli +version: "26.04.00" description: QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use when the user is solving QP from the command line. --- diff --git a/skills/cuopt-qp-api-python/SKILL.md b/skills/cuopt-qp-api-python/SKILL.md index b77e0f649a..b85b9e3db2 100644 --- a/skills/cuopt-qp-api-python/SKILL.md +++ b/skills/cuopt-qp-api-python/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-qp-api-python +version: "26.04.00" description: Quadratic Programming (QP) with cuOpt — Python API only (beta). Use when the user is building or solving QP in Python. --- diff --git a/skills/cuopt-routing-api-python/SKILL.md b/skills/cuopt-routing-api-python/SKILL.md index 89fa7e810e..d8bf736f8f 100644 --- a/skills/cuopt-routing-api-python/SKILL.md +++ b/skills/cuopt-routing-api-python/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-routing-api-python +version: "26.04.00" description: Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use when the user is building or solving routing in Python. --- diff --git a/skills/cuopt-server-api-python/SKILL.md b/skills/cuopt-server-api-python/SKILL.md index 0affe6efee..b340e9883f 100644 --- a/skills/cuopt-server-api-python/SKILL.md +++ b/skills/cuopt-server-api-python/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-server-api-python +version: "26.04.00" description: cuOpt REST server — start server, endpoints, Python/curl client examples. Use when the user is deploying or calling the REST API. --- diff --git a/skills/cuopt-server-common/SKILL.md b/skills/cuopt-server-common/SKILL.md index 01cd9779b1..f23c9c4a5f 100644 --- a/skills/cuopt-server-common/SKILL.md +++ b/skills/cuopt-server-common/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-server-common +version: "26.04.00" description: cuOpt REST server — what it does and how requests flow. Domain concepts; no deploy or client code. --- diff --git a/skills/cuopt-user-rules/SKILL.md b/skills/cuopt-user-rules/SKILL.md index cb5c3c6fc7..7ca291ac9b 100644 --- a/skills/cuopt-user-rules/SKILL.md +++ b/skills/cuopt-user-rules/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-user-rules +version: "26.04.00" description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before any cuOpt user task (routing, LP/MILP, QP, installation, server). Covers handling incomplete questions, clarifying data requirements, verifying understanding, and running commands safely. --- diff --git a/skills/lp-milp-formulation/SKILL.md b/skills/lp-milp-formulation/SKILL.md index 30ea1e6a1e..c0df08f45c 100644 --- a/skills/lp-milp-formulation/SKILL.md +++ b/skills/lp-milp-formulation/SKILL.md @@ -1,5 +1,6 @@ --- name: lp-milp-formulation +version: "26.04.00" description: LP/MILP concepts and going from problem text to formulation. What LP/MILP are, required formulation questions, typical modeling elements, and how to parse problem statements (parameters, constraints, decisions, objective). --- diff --git a/skills/qp-formulation/SKILL.md b/skills/qp-formulation/SKILL.md index 1d6bad6d08..c87b887fbc 100644 --- a/skills/qp-formulation/SKILL.md +++ b/skills/qp-formulation/SKILL.md @@ -1,5 +1,6 @@ --- name: qp-formulation +version: "26.04.00" description: Quadratic Programming (QP) — problem form and constraints. Domain concepts; no API or interface. QP is beta. --- diff --git a/skills/routing-formulation/SKILL.md b/skills/routing-formulation/SKILL.md index 1851125934..4ab8d6419d 100644 --- a/skills/routing-formulation/SKILL.md +++ b/skills/routing-formulation/SKILL.md @@ -1,5 +1,6 @@ --- name: routing-formulation +version: "26.04.00" description: Vehicle routing (VRP, TSP, PDP) — problem types and data requirements. Domain concepts; no API or interface. --- From 5900238ee6c3c661ff5fa8738263123fd69973ed Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 20:31:52 -0600 Subject: [PATCH 32/35] fix style --- .pre-commit-config.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 93688aa567..87a3faaf92 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -95,7 +95,10 @@ repos: [.](md|rst|avro|parquet|png|orc|gz|pkl|sas7bdat|msgpack|pickle|jpg|bz2|zlib)$| ^docs/cuopt/source/cuopt-python/routing/routing-example[.]ipynb$| ^docs/cuopt/source/versions1[.]json$| - ^helmchart/cuopt-server/(Chart[.]yaml|values[.]yaml)$ + ^helmchart/cuopt-server/(Chart[.]yaml|values[.]yaml)$| + ^[.]cursor-plugin/plugin[.]json$| + ^[.]claude-plugin/marketplace[.]json$| + ^gemini-extension[.]json$ - repo: local hooks: - id: update-versions From 7863a3f34c936312d683ee9d0ba61d0665773291 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 3 Mar 2026 23:49:11 -0600 Subject: [PATCH 33/35] fix tests --- ci/test_skills_assets.sh | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh index f40d4c44a7..c75645cb93 100755 --- a/ci/test_skills_assets.sh +++ b/ci/test_skills_assets.sh @@ -72,19 +72,46 @@ while IFS= read -r -d '' script; do fi done < <(find "${SKILLS_ASSETS}" -path "*/assets/*" -name "*.py" -type f -print0 | sort -z) -# ---- C assets (compile and run; requires CONDA_PREFIX) ---- +# ---- C assets (compile and run; requires CONDA_PREFIX and a C compiler) ---- +CC="${CC:-}" +if [[ -z "${CC}" ]]; then + for c in gcc cc clang; do + if command -v "$c" &>/dev/null; then + CC="$c" + break + fi + done +fi if [[ -n "${CONDA_PREFIX:-}" ]]; then + if [[ -z "${CC}" ]]; then + log "No C compiler found; installing c-compiler in conda environment" + if command -v mamba &>/dev/null; then + mamba install -y -c conda-forge c-compiler + else + conda install -y -c conda-forge c-compiler + fi + for c in gcc cc clang; do + if command -v "$c" &>/dev/null; then + CC="$c" + break + fi + done + if [[ -z "${CC}" ]]; then + log "C compiler still not found after install. Set CC or install gcc/cc/clang." + exit 1 + fi + fi INCLUDE_PATH="${CONDA_PREFIX}/include" LIB_PATH="${CONDA_PREFIX}/lib" export LD_LIBRARY_PATH="${LIB_PATH}:${LD_LIBRARY_PATH:-}" - log "Testing C assets in skills/" + log "Testing C assets in skills (using ${CC})" while IFS= read -r -d '' cfile; do dir=$(dirname "$cfile") base=$(basename "$cfile" .c) rel="${cfile#"$REPO_ROOT/"}" log "Building and running C asset: $rel" - if ! (cd "$dir" && gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt); then + if ! (cd "$dir" && "${CC}" -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt); then FAILED+=("$rel (build)") log "FAIL: $rel (build)" continue From f2445198b614e3925f04c8b670ca306ccb5e3171 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 4 Mar 2026 00:32:37 -0600 Subject: [PATCH 34/35] enable test skip for skill --- .github/workflows/pr.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 4d4a507843..4f9496e4d2 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -184,6 +184,10 @@ jobs: - '!sonarqube/**' - '!ucf/**' - '!utilities/**' + - '!skills/**/SKILL.md' + - '!skills/**/resources/**' + - '!ci/utils/validate_skills.sh' + - '!agents/**' test_python_conda: - '**' - '!CONTRIBUTING.md' @@ -218,6 +222,10 @@ jobs: - '!sonarqube/**' - '!ucf/**' - '!utilities/**' + - '!skills/**/SKILL.md' + - '!skills/**/resources/**' + - '!ci/utils/validate_skills.sh' + - '!agents/**' test_python_wheels: - '**' - '!CONTRIBUTING.md' @@ -253,6 +261,10 @@ jobs: - '!sonarqube/**' - '!ucf/**' - '!utilities/**' + - '!skills/**/SKILL.md' + - '!skills/**/resources/**' + - '!ci/utils/validate_skills.sh' + - '!agents/**' checks: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@main From bb9a0d324916723d124816f0ed3ec02e72f2e78f Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 4 Mar 2026 00:41:55 -0600 Subject: [PATCH 35/35] add missing set of files --- .github/workflows/pr.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 4f9496e4d2..52dfa3b60e 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -187,7 +187,11 @@ jobs: - '!skills/**/SKILL.md' - '!skills/**/resources/**' - '!ci/utils/validate_skills.sh' + - '!ci/utils/sync_skills_version.sh' - '!agents/**' + - '!.cursor-plugin/**' + - '!.claude-plugin/**' + - '!gemini-extension.json' test_python_conda: - '**' - '!CONTRIBUTING.md' @@ -225,7 +229,11 @@ jobs: - '!skills/**/SKILL.md' - '!skills/**/resources/**' - '!ci/utils/validate_skills.sh' + - '!ci/utils/sync_skills_version.sh' - '!agents/**' + - '!.cursor-plugin/**' + - '!.claude-plugin/**' + - '!gemini-extension.json' test_python_wheels: - '**' - '!CONTRIBUTING.md' @@ -264,7 +272,11 @@ jobs: - '!skills/**/SKILL.md' - '!skills/**/resources/**' - '!ci/utils/validate_skills.sh' + - '!ci/utils/sync_skills_version.sh' - '!agents/**' + - '!.cursor-plugin/**' + - '!.claude-plugin/**' + - '!gemini-extension.json' checks: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@main