cuopt-numerical-optimization-api-python

Par nvidia · skills

Résolvez des problèmes LP, MILP, QP (bêta) avec l'API Python cuOpt — objectifs linéaires/quadratiques, variables entières, ordonnancement, portefeuille, moindres carrés.

npx skills add https://github.com/nvidia/skills --skill cuopt-numerical-optimization-api-python

Skill de Optimisation Numérique cuOpt (Python)

Modélisez et résolvez des problèmes LP, MILP et QP en utilisant le solveur GPU-accéléré d'NVIDIA cuOpt. La surface API Python (Problem, SolverSettings, solve) est partagée entre les trois classes de problèmes — seule la forme de l'objectif et quelques règles changent.

Avant de commencer

Utilisez un résumé de formulation (paramètres, contraintes, décisions, objectif) s'il existe ; sinon, demandez les variables de décision, l'objectif et les contraintes. Confirmez ensuite le type de problème (LP / MILP / QP — voir ci-dessous) et les types de variables.

Choisir entre LP, MILP et QP

Décidez à partir de l'objectif et des variables :

Si l'objectif est... Et les variables sont... Utilisez
Linéaire (somme de c_i * x_i) Toutes continues LP
Linéaire Certaines entières ou binaires MILP
Contient des termes carrés (x*x) ou croisés (x*y) Continues (QP entier non supporté) QP (bêta)

Privilégiez LP quand le problème le permet. LP se résout plus vite et offre des garanties d'optimalité plus fortes. N'utilisez MILP que si le problème nécessite logiquement des nombres entiers ou des décisions oui/non. Utilisez QP uniquement si l'objectif est véritablement quadratique (variance, erreur quadratique, énergie cinétique).

Types de problèmes demandant une vigilance particulière : La planification multi-période et la programmation par objectifs sont faciles à mal interpréter. Vérifiez bien que les taux et contraintes s'appliquent à la bonne période ou au bon niveau de priorité (AGENTS.md : vérifiez votre compréhension avant de coder).

  • Utilisez LP quand chaque quantité peut être significativement fractionnaire : flux, proportions, taux, dollars, heures, tonnes de matériel, etc.
  • Utilisez MILP quand le problème mentionne des comptes d'entités discrètes, des choix oui/non, ou des décisions soit/soit (par ex. ouvrir une installation ou non, assigner une personne à un quart, nombre de camions).
  • Utilisez QP quand l'objectif minimise la variance, l'erreur quadratique, ou toute expression avec des termes x*x ou x*y (optimisation de portefeuille, moindres carrés, régression régularisée).

Entier vs continu selon la formulation

Choisissez le type de variable en fonction de ce que décrit le problème.

Formulation / concept du problème Type de variable Exemples
Entités discrètes (comptes) INTEGER Travailleurs, voitures, camions, machines, pilotes, installations, unités à fabriquer (quand « unités » signifie des articles entiers), stagiaires, véhicules
Oui/non ou marche/arrêt INTEGER (binaire, lb=0 ub=1) Ouvrir une installation, faire fonctionner une machine, produire une ligne de produits, assigner une personne à un quart
Quantités pouvant être fractionnaires CONTINUOUS Tonnes, litres, dollars, heures, kWh, proportion de capacité, volume de flux, poids
Taux ou fractions CONTINUOUS Utilisation, pourcentage, part du budget
Incertain Préférez INTEGER si le nom est une chose dénombrable (un travailleur, une voiture) ; préférez CONTINUOUS si c'est une mesure (quantité d'acier, heures travaillées). Si le problème dit « entier » ou « nombre de », utilisez INTEGER.

Règle empirique : Si la quantité est « combien de choses » (personnes, véhicules, articles, sites), utilisez INTEGER. Si c'est « combien » (masse, volume, argent, temps) ou un taux, utilisez CONTINUOUS sauf si le problème exige explicitement des nombres entiers.

Référence rapide : API Python

Exemple LP

from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE
from cuopt.linear_programming.solver_settings import SolverSettings

# Créer le problème
problem = Problem("MyLP")

# Variables de décision
x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x")
y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y")

# Contraintes
problem.addConstraint(2*x + 3*y <= 120, name="resource_a")
problem.addConstraint(4*x + 2*y <= 100, name="resource_b")

# Objectif
problem.setObjective(40*x + 30*y, sense=MAXIMIZE)

# Résoudre
settings = SolverSettings()
settings.set_parameter("time_limit", 60)
problem.solve(settings)

# Vérifier le statut (CRITIQUE : utiliser PascalCase !)
if problem.Status.name in ["Optimal", "PrimalFeasible"]:
    print(f"Objectif: {problem.ObjValue}")
    print(f"x = {x.getValue()}")
    print(f"y = {y.getValue()}")

Exemple MILP (avec variables entières)

from cuopt.linear_programming.problem import Problem, CONTINUOUS, INTEGER, MINIMIZE

problem = Problem("FacilityLocation")

# Variable binaire (entière avec bornes 0-1)
open_facility = problem.addVariable(lb=0, ub=1, vtype=INTEGER, name="open")

# Variable continue
production = problem.addVariable(lb=0, vtype=CONTINUOUS, name="production")

# Contrainte de liaison : on ne peut produire que si l'installation est ouverte
problem.addConstraint(production <= 1000 * open_facility, name="link")

# Objectif : coût fixe + coût variable
problem.setObjective(500*open_facility + 2*production, sense=MINIMIZE)

# Paramètres spécifiques à MILP
settings = SolverSettings()
settings.set_parameter("time_limit", 120)
settings.set_parameter("mip_relative_gap", 0.01)  # Écart d'optimalité de 1 %

problem.solve(settings)

# Vérifier le statut
if problem.Status.name in ["Optimal", "FeasibleFound"]:
    print(f"Ouverture de l'installation: {open_facility.getValue() > 0.5}")
    print(f"Production: {production.getValue()}")

Exemple QP (bêta — MINIMIZE uniquement)

from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE
from cuopt.linear_programming.solver_settings import SolverSettings

# Minimisation de la variance du portefeuille
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")

# Objectif quadratique (variance) — DOIT être MINIMIZE
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,
)

# Contraintes linéaires
problem.addConstraint(x1 + x2 + x3 == 1, name="budget")
problem.addConstraint(0.12*x1 + 0.08*x2 + 0.05*x3 >= 0.08, name="min_return")

problem.solve(SolverSettings())
if problem.Status.name in ["Optimal", "PrimalFeasible"]:
    print(f"Variance: {problem.ObjValue}")

Règles pour QP :

  • MINIMIZE uniquement — le solveur rejette MAXIMIZE pour les objectifs quadratiques. Pour maximiser f(x), minimisez -f(x).
  • Variables continues uniquement — QP entier n'est pas supporté.
  • Q doit être définie positive (PSD) pour un problème convexe ; sinon le solveur peut renvoyer un point stationnaire non optimal.
  • Bêta — l'API peut évoluer ; utilisable en production pour les QP convexes typiques mais attendez des changements occasionnels.

Consultez references/qp_examples.md pour des exemples de moindres carrés, contournement de maximisation et forme matricielle.

CRITIQUE : Vérification du statut

Les valeurs de statut utilisent PascalCase, PAS de majuscules :

# ✅ CORRECT
if problem.Status.name in ["Optimal", "FeasibleFound"]:
    print(problem.ObjValue)

# ❌ FAUX - échouera silencieusement !
if problem.Status.name == "OPTIMAL":  # Ne correspondra jamais !
    print(problem.ObjValue)

Valeurs de statut LP : Optimal, NoTermination, NumericalError, PrimalInfeasible, DualInfeasible, IterationLimit, TimeLimit, PrimalFeasible

Valeurs de statut MILP : Optimal, FeasibleFound, Infeasible, Unbounded, TimeLimit, NoTermination

Valeurs de statut QP : Même ensemble que LP. Pour déboguer QP, affichez f"Statut réel: '{problem.Status.name}'" et vérifiez que Q est PSD et que les variables sont raisonnablement mises à l'échelle.

Motifs de modélisation courants

Sélection binaire

# Sélectionner exactement k articles parmi n
items = [problem.addVariable(lb=0, ub=1, vtype=INTEGER) for _ in range(n)]
problem.addConstraint(sum(items) == k)

Liaison Big-M

# Si y=1, alors x <= 100 ; si y=0, x peut être n'importe quoi jusqu'à M
M = 10000
problem.addConstraint(x <= 100 + M*(1 - y))

Si-alors « doit aussi produire »

Quand le problème dit si on fait X alors on doit aussi faire Y, appliquez les deux : (i) la liaison binaire et (ii) que Y est réellement produit :

# y_X <= y_Y (si on fait X, on doit "faire" Y)
problem.addConstraint(y_X <= y_Y)
# Production de Y quand Y est choisi : produire au moins 1 (ou un minimum) quand y_Y=1
problem.addConstraint(production_Y >= 1 * y_Y)  # ou min_amount * y_Y

Sinon le solveur peut définir y_Y=1 mais production_Y=0, satisfaisant la liaison binaire mais pas l'intention.

Construire des expressions volumineuses

Chaîner + sur de nombreux termes peut atteindre les limites de récursion de l'API. Préférez construire objectifs et contraintes avec LinearExpression :

from cuopt.linear_programming.problem import LinearExpression

# Construire comme liste de (vars, coeffs) au lieu de 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)

Consultez les modèles de référence dans le répertoire assets/ de cette skill pour des exemples.

Linéaire par morceaux (SOS2)

# Approximer une fonction non linéaire avec des points de rupture
# Utiliser des variables lambda qui somment à 1, au maximum 2 non-zéro adjacents

Paramètres du solveur

settings = SolverSettings()

# Limite de temps
settings.set_parameter("time_limit", 60)

# Tolérance d'écart MILP (arrêter quand à X% de l'optimal)
settings.set_parameter("mip_relative_gap", 0.01)

# Journalisation
settings.set_parameter("log_to_console", 1)

Problèmes courants

Problème Cause probable Solution
Le statut n'est jamais « OPTIMAL » Utilise la mauvaise casse Utilisez "Optimal" et non "OPTIMAL"
Variable entière a une valeur fractionnaire Définie comme CONTINUOUS Utilisez vtype=INTEGER
Infaisable Contraintes conflictuelles Vérifiez la logique des contraintes
Non limité Bornes manquantes Ajoutez des bornes aux variables
Résolution lente Problème volumineux Définissez une limite de temps, augmentez la tolérance d'écart
Profondeur de récursion maximale Construction d'une grande expression avec + chaîné Utilisez LinearExpression(vars_list, coeffs_list, constant)
QP rejeté avec MAXIMIZE QP ne supporte que MINIMIZE Négativement l'objectif : minimisez -f(x)
QP renvoie non optimal Q pas PSD ou variables mal mises à l'échelle Vérifiez que Q est PSD ; remettez à l'échelle les variables à des magnitudes similaires

Obtenir les valeurs duales (LP / QP)

Les duales et coûts réduits sont renvoyés pour LP et QP. Ils ne sont pas renvoyés pour un problème avec contraintes quadratiques (chaque valeur revient comme NaN), donc les lisez uniquement quand toutes les contraintes sont linéaires. MILP ne renvoie aucune duale.

if problem.Status.name == "Optimal":
    constraint = problem.getConstraint("resource_a")   # contrainte linéaire
    print(f"Valeur duale: {constraint.DualValue}")       # NaN si le modèle a des contraintes quadratiques

Modèles de référence

Tous les modèles de référence se trouvent dans le répertoire assets/ de cette skill. Utilisez-les comme référence lors de la construction de nouvelles applications ; ne les modifiez pas sur place.

Exemples minimaux / canoniques (LP, MILP, QP)

Modèle Type Description
lp_basic LP LP minimal : variables, contraintes, objectif, résolution
lp_duals LP Valeurs duales et coûts réduits
lp_warmstart LP Démarrage chaud PDLP pour problèmes similaires
milp_basic MILP MIP minimal ; inclut un exemple de rappel incumbent
milp_production_planning MILP Planification de la production avec contraintes de ressources
portfolio QP Minimiser la variance du portefeuille ; contraintes de budget et rendement min
least_squares QP Minimiser (x-3)² + (y-4)² (point le plus proche)
maximization_workaround QP Maximiser le quadratique via minimiser -f(x)

Autre référence

Modèle Type Description
mps_solver LP/MILP Résoudre tout problème depuis le format MPS standard

Commande rapide pour lister les modèles : ls assets/ (depuis le répertoire de cette skill).

Quand escalader

Utilisez le dépannage et les conseils de diagnostic si :

  • Infaisable et vous ne pouvez pas déterminer pourquoi
  • Problèmes numériques

Skills similaires