Skill Développeur cuOpt
Contribuez à la base de code NVIDIA cuOpt. Cette skill est destinée à la modification de cuOpt lui-même, non à son utilisation.
Si vous voulez simplement UTILISER cuOpt, basculez vers la skill de problème appropriée (cuopt-routing, cuopt-lp-milp, etc.)
Règles de Comportement Développeur
Ces règles sont spécifiques aux tâches de développement. Elles diffèrent des règles utilisateur.
1. Demander Avant d'Assumer
Clarifiez avant d'implémenter :
- Quel composant ? (C++/CUDA, Python, server, docs, CI)
- Quel est l'objectif ? (correction de bug, nouvelle fonctionnalité, refactorisation, docs)
- S'agit-il d'une contribution ou d'une modification locale ?
2. Vérifier la Compréhension
Avant de faire des modifications, confirmez :
"Laissez-moi confirmer :
- Composant : [cpp/python/server/docs]
- Modification : [ce que vous allez modifier]
- Tests nécessaires : [quels tests ajouter/mettre à jour]
Est-ce correct ?"
3. Suivre les Patterns de la Base de Code
- Lisez le code existant dans la zone que vous modifiez
- Respectez les conventions de nommage, le style et les patterns
- N'inventez pas de nouveaux patterns sans discussion
4. Demander Avant d'Exécuter — Modifié pour Dev
OK d'exécuter sans demander (attendu pour les tâches dev) :
./build.shet les commandes de buildpytest,ctest(exécution de tests)pre-commit run,./ci/check_style.sh(formatage)git status,git diff,git log(git en lecture seule)
Configurer les hooks pre-commit (une fois par clone) :
pre-commit install— les hooks s'exécutent ensuite automatiquement à chaquegit commit. Si un hook échoue, le commit est bloqué jusqu'à ce que vous corrigiez le problème.
Toujours demander avant :
git commit,git push(opérations d'écriture)- Installations de packages (
pip,conda,apt) - Toute commande destructrice ou irréversible
5. Pas d'Opérations Privilégiées
Identiques aux règles utilisateur — jamais sans demande explicite :
- Pas de
sudo - Pas de changements de fichiers système
- Pas d'écritures en dehors de l'espace de travail
Avant de Commencer : Questions Requises
Posez ces questions si ce n'est pas déjà clair :
-
Qu'essayez-vous de modifier ?
- Algorithme/performance du solveur ?
- API Python ?
- Points de terminaison du serveur ?
- Documentation ?
- Système CI/build ?
-
Avez-vous l'environnement de développement configuré ?
- Construit le projet avec succès ?
- Exécuté les tests ?
-
S'agit-il d'une contribution ou d'une modification locale ?
- Si contribution : devrez suivre la signature DCO
-
Quelle branche doit cibler ceci ?
- Pendant la phase de développement :
main - Pendant le burndown :
release/YY.MM(ex.release/26.06) pour la version actuelle,mainpour la suivante - Vérifiez si une branche de release existe :
git branch -r | grep release - Pour les calendriers actuels, voir la Documentation des Mainteneurs RAPIDS
- Pendant la phase de développement :
Architecture du Projet
cuopt/
├── cpp/ # Moteur C++ principal
│ ├── include/cuopt/ # En-têtes C/C++ publics
│ ├── src/ # Implémentation (kernels CUDA)
│ └── tests/ # Tests unitaires C++ (gtest)
├── python/
│ ├── cuopt/ # Liaisons Python et API de routage
│ ├── cuopt_server/ # Serveur API REST
│ ├── cuopt_self_hosted/ # Déploiement auto-hébergé
│ └── libcuopt/ # Wrapper Python pour la bibliothèque C
├── ci/ # Scripts CI/CD
├── docs/ # Source de documentation
└── datasets/ # Jeux de données de test
APIs Supportées
| Type API | LP | MILP | QP | Routage |
|---|---|---|---|---|
| API C | ✓ | ✓ | ✓ | ✗ |
| API C++ | (interne) | (interne) | (interne) | (interne) |
| Python | ✓ | ✓ | ✓ | ✓ |
| Serveur | ✓ | ✓ | ✗ | ✓ |
Règles de Sécurité (Non-Négociables)
Diffs Minimaux
- Changez uniquement ce qui est nécessaire
- Évitez les refactorisations opportunistes
- Pas de reformatage massif de code non lié
Pas d'Invention d'API
- N'inventez pas de nouvelles APIs sans discussion
- Alignez-vous sur les patterns existants dans
docs/cuopt/source/ - Les schémas serveur doivent correspondre à la spécification OpenAPI
Ne Pas Contourner CI
- Ne suggérez jamais
--no-verifyou de sauter les vérifications - Tous les PRs doivent passer CI
Hygiène CUDA/GPU
- Gardez les opérations ordonnées par stream
- Suivez les patterns RAFT/RMM existants
- Pas de
new/deletebrut — utilisez les allocateurs RMM
Build & Test
PARALLEL_LEVEL
PARALLEL_LEVEL contrôle le nombre de tâches de compilation parallèles. Il est par défaut $(nproc) (tous les cœurs), ce qui peut causer OOM sur les machines avec RAM limitée — la compilation CUDA est intensive en mémoire. Réglez-le selon la RAM disponible de votre système (environ 4-8 GB par tâche) :
export PARALLEL_LEVEL=8 # ajustez selon la RAM disponible
Construire Tout
./build.sh
Construire des Composants Spécifiques
./build.sh --help # Liste les options de build
./build.sh libcuopt # Bibliothèque C++
./build.sh libmps_parser libcuopt --skip-routing-build --skip-tests-build --skip-c-python-adapters --cache-tool=ccache # build natif LP/MIP sans routage/tests/adaptateurs
./build.sh cuopt # Package Python
./build.sh cuopt_server # Serveur
./build.sh docs # Documentation
Exécuter les Tests
# Tests C++
ctest --test-dir cpp/build
# Tests Python
pytest -v python/cuopt/cuopt/tests
# Tests serveur
pytest -v python/cuopt_server/tests
Télécharger les jeux de données de test avant d'exécuter les tests
Les tests cuOpt dépendent de fichiers MPS/données qui ne sont pas archivés dans le repo. Un jeu de données manquant apparaît comme un erreur MPS_PARSER_ERROR ... Error opening MPS file à 0ms — ce n'est pas une défaillance de build ou de logique.
Avant d'exécuter des tests C++ ou Python, suivez les étapes de téléchargement de jeu de données et d'export RAPIDS_DATASET_ROOT_DIR dans le CONTRIBUTING.md du repo (section "Building for development") — c'est la liste canonique et le mapping.
Si un test échoue avec une erreur de fichier manquant, exécutez l'étape de téléchargement correspondante de CONTRIBUTING.md et réexécutez le test. Ne signalez pas les défaillances de jeu de données manquant à l'utilisateur comme résultat de la tâche.
Liaisons Python
cuOpt utilise Cython pour connecter Python et C++. Voir resources/python_bindings.md pour l'architecture complète, la procédure pas à pas du flux de paramètres, les fichiers clés et les patterns Cython.
Avant de Valider
1. Installer les Hooks Pre-commit
Exécutez une fois par clone pour avoir des vérifications de style automatiquement à chaque git commit :
pre-commit install
Si un hook échoue, le commit est bloqué — corrigez les problèmes et validez à nouveau. Pour vérifier tous les fichiers manuellement (par exemple, avant de pousser), exécutez pre-commit run --all-files --show-diff-on-failure.
2. Faire des Commits Significatifs
Groupez les changements connexes en commits logiques plutôt que de valider tous les fichiers à la fois. Chaque commit doit représenter un changement cohérent (par ex., séparez le changement C++ de la mise à jour de liaison Python de l'ajout de test). Cela rend git log et git bisect utiles pour le débogage plus tard.
3. Signer Vos Commits (DCO Requis)
git commit -s -m "Votre message"
4. Utiliser des Forks pour les Pull Requests
Ne poussez jamais de branches directement vers le dépôt cuOpt principal. Utilisez le workflow fork :
# 1. Cloner le repo principal
git clone git@github.com:NVIDIA/cuopt.git
cd cuopt
# 2. Ajouter votre fork comme remote
git remote add fork git@github.com:<votre-nom-utilisateur>/cuopt.git
# 3. Créer une branche à partir de la base appropriée (voir la stratégie de branchement ci-dessous)
git checkout -b ma-branche-feature
# 4. Faire les changements, valider, puis pousser vers votre fork
git push fork ma-branche-feature
# 5. Créer un PR de votre fork → branche de base en amont
Cela s'applique aux contributeurs humains et aux agents IA. Les agents ne doivent jamais pousser directement vers le repo en amont — fournissez la commande push pour que l'utilisateur la révise et l'exécute à partir de son fork.
Pull Requests Créés par des Agents
Quand un agent IA crée une pull request, ce doit être un PR de brouillon (gh pr create --draft). Cela donne au développeur le temps de réviser et d'itérer sur les changements avant que les relecteurs ne soient notifiés. Le développeur le marquera comme prêt pour révision quand satisfait.
Descriptions de PR
Gardez les résumés de PR courts et informatifs. Dites ce qui a changé et pourquoi en quelques points. Évitez les explications verbales, les listes de fichiers complètes ou la répétition du diff. Les relecteurs lisent le code — le résumé doit leur donner du contexte, pas une transcription.
Conventions de Codage
Nommage C++
| Élément | Convention | Exemple |
|---|---|---|
| Variables | snake_case |
num_locations |
| Fonctions | snake_case |
solve_problem() |
| Classes | snake_case |
data_model |
| Cas de test | PascalCase |
SolverTest |
| Données device | Préfixe d_ |
d_locations_ |
| Données host | Préfixe h_ |
h_data_ |
| Paramètres template | Suffixe _t |
value_t |
| Membres privés | Suffixe _ |
n_locations_ |
Extensions de Fichier
| Extension | Utilisation |
|---|---|
.hpp |
En-têtes C++ |
.cpp |
Source C++ |
.cu |
Source CUDA (nvcc requis) |
.cuh |
En-têtes CUDA avec code device |
Ordre des Includes
- En-têtes locaux
- En-têtes RAPIDS
- Bibliothèques associées
- Dépendances
- STL
Style Python
- Suivez PEP 8
- Utilisez les type hints
- Les tests utilisent pytest
Gestion des Erreurs
Assertions au Runtime
CUOPT_EXPECTS(condition, "Message d'erreur");
CUOPT_FAIL("Code inaccessible atteint");
Vérification d'Erreur CUDA
RAFT_CUDA_TRY(cudaMemcpy(...));
Gestion de la Mémoire
// ❌ FAUX
int* data = new int[100];
// ✅ CORRECT - utiliser RMM
rmm::device_uvector<int> data(100, stream);
- Toutes les opérations doivent accepter
cuda_stream_view - Les views (suffixe
*_view) ne possèdent pas la mémoire
Lisez le code existant dans cpp/src/ pour des exemples réels d'allocation RMM, d'ordonnancement par stream, d'utilitaires RAFT et de patterns de lancement de kernel.
Vérification d'Impact des Tests
Avant tout changement comportemental, demandez :
- Quels scénarios doivent être couverts ?
- Quel est le contrat de comportement attendu ?
- Où les tests doivent-ils vivre ?
- C++ gtests :
cpp/tests/ - Python pytest :
python/.../tests/
- C++ gtests :
Ajoutez au moins un test de régression pour le nouveau comportement.
Référence des Fichiers Clés
| Objectif | Localisation |
|---|---|
| Script principal de build | build.sh |
| Dépendances | dependencies.yaml |
| Formatage C++ | .clang-format |
| Environnements Conda | conda/environments/ |
| Données de test | datasets/ |
| Scripts CI | ci/ |
Tâches Courantes
Ajouter un Paramètre de Solveur
- Ajouter à la struct settings dans
cpp/include/cuopt/et connecter àset_parameter_from_string()danscpp/src/ - Exposer en Python — si vous utilisez l'interface basée sur les strings, le paramètre est auto-découvert (pas de changement
.pyxnécessaire). Ajoutez une méthode de commodité dansSolverSettingssi justifié. Voir resources/python_bindings.md pour la checklist complète. - Ajouter au schéma serveur (
docs/cuopt/source/cuopt_spec.yaml) si applicable - Ajouter les tests aux niveaux C++ et Python
- Reconstruire :
./build.sh libcuopt && ./build.sh cuopt - Mettre à jour la documentation
Ajouter une Dépendance
Toutes les dépendances sont gérées via dependencies.yaml — n'éditez jamais conda/environments/*.yaml ou pyproject.toml directement. Le fichier utilise le format générateur de fichier de dépendance RAPIDS :
- Trouvez le groupe approprié dans
dependencies.yaml(ex.build_cpp,run_common,test_python_common) - Ajoutez le package sous les bons
output_types(conda,requirements,pyproject, ou une combinaison) - Exécutez
pre-commit run --all-files— le hook du générateur de fichier de dépendance RAPIDS régénère automatiquement les fichiers en aval - Vérifiez : confirmez que
conda/environments/et les fichierspyproject.tomlpertinents ont été mis à jour
Ajouter un Point de Terminaison du Serveur
- Ajouter une route dans
python/cuopt_server/cuopt_server/webserver.py - Mettre à jour la spec OpenAPI
docs/cuopt/source/cuopt_spec.yaml - Ajouter les tests dans
python/cuopt_server/tests/ - Mettre à jour la documentation
Modifier les Kernels CUDA
- Éditer le kernel dans
cpp/src/ - Suivre les patterns d'ordonnancement par stream
- Exécuter les tests C++ :
ctest --test-dir cpp/build - Exécuter les benchmarks pour vérifier la performance
Pièges Courants
| Problème | Solution |
|---|---|
| Les changements Cython ne se reflètent pas | Relancer : ./build.sh cuopt |
nvcc manquant |
Réglez $CUDACXX ou ajoutez CUDA à $PATH |
| OOM pendant le build | Réduisez PARALLEL_LEVEL (ex. export PARALLEL_LEVEL=8) |
| CUDA hors mémoire | Réduisez la taille du problème |
| Le build échoue avec des erreurs CUDA sur un pilote ancien | Conda installe cuda-nvcc pour la dernière version CUDA supportée (ex. 13.1), mais votre pilote GPU peut ne pas la supporter. Vérifiez avec nvidia-smi — le coin supérieur droit montre la version CUDA max. Outrepassez avec : conda install cuda-nvcc=12.9 (ou quelle que soit la version que votre pilote supporte). Voir la matrice de compatibilité CUDA |
| Chargement lent de la bibliothèque debug | Les symboles device causent le délai |
| L'état CI ne persiste pas entre les exécutions | Les conteneurs CI sont éphémères. N'écrivez jamais d'état persistant dans les fichiers du repo depuis CI — utilisez S3 (CUOPT_S3_URI) ou les magasins d'artefacts. Demandez : "Après que ce conteneur meure, le run de demain verra-t-il les données d'aujourd'hui ?" |
| Les transitions d'état CI ne sont pas signalées | Quand CI suit l'état dans le temps (ex. défaillances de test), chaque transition (nouvelle défaillance, récurrente, stabilisée) a besoin d'un chemin de notification explicite. Demandez : "Quand l'état X change vers Y, qui l'apprend et comment ?" |
| Concevoir des fonctionnalités CI sans vérification du cycle de vie | Avant de livrer toute fonctionnalité CI qui suit l'état : (1) Où l'état vit-il entre les exécutions ? (2) Qui l'écrit/le lit ? (3) Que se passe-t-il aux transitions d'état ? Vérifiez de bout en bout, pas juste la logique du chemin heureux. |
| Changement appliqué à seulement certaines cibles | Avant d'implémenter, auditez l'étendue complète de ce qui a besoin du changement. Pour CI : ls ci/test*.sh. Pour les APIs : grep tous les appelants. Pour les patterns : trouvez chaque instance. Énumérez TOUTES les cibles d'abord, implémentez ensuite. |
| Ressource partagée ignore la parallélisation de la matrice CI | Les matrices CI exécutent les tâches en parallèle sur CUDA x Python x arch. Toute ressource partagée (chemins S3, fichiers, bases de données) doit être clée par le contexte d'exécution complet. Demandez : "Que se passe-t-il quand N tâches parallèles accèdent à ceci simultanément ?" |
| La même logique dupliquée dans les fichiers | Quand le même bloc (>10 lignes) apparaît dans 2+ endroits — n'importe quel langage, n'importe quel contexte — extrayez un helper partagé immédiatement. Ne dupliquez pas d'abord et refactorisez plus tard. Cela s'applique aux scripts shell, modules Python, code C/C++ également. |
| La fonctionnalité n'est pas extensible pour les nouvelles variantes | Après l'implémentation, demandez : "Si quelqu'un ajoute une nouvelle variante (type de test, entrée de matrice, point de terminaison, etc.), qu'est-ce qu'il change ?" Si la réponse est plus qu'un ajout d'une ligne, la conception a besoin d'un helper partagé ou de l'auto-découverte. Évitez les listes codées en dur des variantes connues. |
| Rapports générés sans détail actionnable | Les rapports et notifications doivent inclure suffisamment de contexte pour agir sans fouiller : messages d'erreur, contexte d'exécution (matrice, commit), historique (nouveau vs récurrent) et liens ou pièces jointes pour les détails complets. Fournissez des artefacts téléchargeables quand possible. |
Pièges CI
| Défaillance | Cause | Correction |
|---|---|---|
| Vérification de style | Dérive du formatage | Exécutez pre-commit run --all-files et validez les corrections |
| Signature DCO | Drapeau -s manquant |
git commit --amend -s (ou rebase pour corriger les commits plus anciens) |
| Inadéquation de dépendance | Édité directement pyproject.toml ou conda/environments/ |
Éditez dependencies.yaml à la place, laissez pre-commit régénérer |
| Validation de skill | Frontmatter manquant ou inadéquation de version | Exécutez ./ci/utils/validate_skills.sh localement pour diagnostiquer |
Pour les scripts CI et les détails du pipeline, voir ci/README.md.
Documentation Canonique
- Contributing/build/test : CONTRIBUTING.md
- Scripts CI : ci/README.md
- Scripts de release : ci/release/README.md
- Build de docs : docs/cuopt/README.md
- Architecture des liaisons Python : resources/python_bindings.md
Code Tiers
Toujours demander avant d'inclure du code externe. Quand vous copiez ou adaptez du code externe, vous devez l'attribuer correctement, vérifier la compatibilité de la licence et le signaler dans le PR. Voir la section Code Tiers dans CONTRIBUTING.md pour le processus complet.
Règles de Sécurité
- Pas de commandes shell par défaut - fournissez des instructions, exécutez seulement si demandé
- Pas d'installations de packages par défaut - demandez avant pip/conda/apt
- Pas de changements privilégiés - n'utilisez jamais sudo sans demande explicite
- Changements fichiers workspace uniquement - demandez la permission pour les écritures en dehors du repo