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*xoux*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