cuopt-developer

Par nvidia · skills

Contribuez à la base de code NVIDIA cuOpt, notamment en C++/CUDA, Python, serveur, documentation et CI. À utiliser lorsque l'utilisateur souhaite modifier les composants internes du solver, ajouter des fonctionnalités, soumettre des pull requests ou comprendre l'architecture de la base de code.

npx skills add https://github.com/nvidia/skills --skill cuopt-developer

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.sh et les commandes de build
  • pytest, 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 à chaque git 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 :

  1. Qu'essayez-vous de modifier ?

    • Algorithme/performance du solveur ?
    • API Python ?
    • Points de terminaison du serveur ?
    • Documentation ?
    • Système CI/build ?
  2. Avez-vous l'environnement de développement configuré ?

    • Construit le projet avec succès ?
    • Exécuté les tests ?
  3. S'agit-il d'une contribution ou d'une modification locale ?

    • Si contribution : devrez suivre la signature DCO
  4. 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, main pour 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

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-verify ou 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/delete brut — 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

  1. En-têtes locaux
  2. En-têtes RAPIDS
  3. Bibliothèques associées
  4. Dépendances
  5. 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 :

  1. Quels scénarios doivent être couverts ?
  2. Quel est le contrat de comportement attendu ?
  3. Où les tests doivent-ils vivre ?
    • C++ gtests : cpp/tests/
    • Python pytest : python/.../tests/

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

  1. Ajouter à la struct settings dans cpp/include/cuopt/ et connecter à set_parameter_from_string() dans cpp/src/
  2. Exposer en Python — si vous utilisez l'interface basée sur les strings, le paramètre est auto-découvert (pas de changement .pyx nécessaire). Ajoutez une méthode de commodité dans SolverSettings si justifié. Voir resources/python_bindings.md pour la checklist complète.
  3. Ajouter au schéma serveur (docs/cuopt/source/cuopt_spec.yaml) si applicable
  4. Ajouter les tests aux niveaux C++ et Python
  5. Reconstruire : ./build.sh libcuopt && ./build.sh cuopt
  6. 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 :

  1. Trouvez le groupe approprié dans dependencies.yaml (ex. build_cpp, run_common, test_python_common)
  2. Ajoutez le package sous les bons output_types (conda, requirements, pyproject, ou une combinaison)
  3. 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
  4. Vérifiez : confirmez que conda/environments/ et les fichiers pyproject.toml pertinents ont été mis à jour

Ajouter un Point de Terminaison du Serveur

  1. Ajouter une route dans python/cuopt_server/cuopt_server/webserver.py
  2. Mettre à jour la spec OpenAPI docs/cuopt/source/cuopt_spec.yaml
  3. Ajouter les tests dans python/cuopt_server/tests/
  4. Mettre à jour la documentation

Modifier les Kernels CUDA

  1. Éditer le kernel dans cpp/src/
  2. Suivre les patterns d'ordonnancement par stream
  3. Exécuter les tests C++ : ctest --test-dir cpp/build
  4. 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

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

Skills similaires