Orchestration multi-agent pour la conception autonome d'expériences sur atomes neutres.
CryoSwarm-Q, c'est la couche logicielle qui se place entre l'intention du chercheur et la programmation pulse-level sur processeurs à atomes neutres. Tu lui donnes un objectif expérimental structuré, il génère des registres atomiques candidats, des séquences de pulses, il évalue tout ça sous bruit réaliste, il classe par robustesse et faisabilité, et il retient les leçons pour la prochaine campagne.
Le système cible les processeurs Pasqal à atomes de
- Pourquoi CryoSwarm-Q — Le problème avant la solution
- Fondations mathématiques — Les équations qu'on respecte
- Architecture multi-agent — La chaîne de commandement
- Pipeline ML — Quand le réseau apprend le métier
- Dashboard interactif — Le centre de contrôle
- API REST — Pour ceux qui préfèrent les endpoints
- Installation — Trois commandes et c'est parti
- Démarrage rapide — Du concret, vite
- Usage programmatique — Pour les scripteurs
- Suite de tests — 372 tests, zéro regret
- Cartographie du repo — Plan de la base
- Limitations actuelles — Ce qu'on ne prétend pas faire
- Licence
Designer une expérience sur plateforme à atomes neutres, c'est un peu comme Boltzmann face à l'entropie : le problème est clair, mais l'espace des solutions est immense. Le placement des atomes est programmable, le contrôle des pulses est flexible, et les contraintes hardware sont strictes (rayon de blocage, bornes sur la fréquence de Rabi, géométrie du device). Ajoutons le bruit — fluctuations laser, déphasage, erreurs SPAM, désordre de position — et on obtient un problème d'optimisation que personne ne résout à la main au-delà de quelques atomes.
CryoSwarm-Q automatise la boucle complète de conception :
- Cadrage du problème — traduire un objectif scientifique en spécification d'expérience concrète.
- Génération de géométrie — proposer des registres atomiques qui respectent les contraintes hardware (espacement minimal, conditions de blocage, bornes du device).
- Design de séquences de pulses — générer des candidats sur 5 familles de formes d'onde, par heuristiques, apprentissage par renforcement ou stratégies hybrides.
- Évaluation sous bruit — simuler chaque candidat en conditions nominales et perturbées (bruit d'amplitude, dérive du désaccord, déphasage, perte d'atomes, SPAM, inhomogénéité spatiale).
- Classement par robustesse — scorer et classer les candidats selon un objectif composite qui balance alignement de l'observable, robustesse, coût d'exécution et latence.
- Mémoire — stocker les leçons apprises pour que les campagnes futures démarrent avec du contexte.
L'objectif n'est pas de remplacer le physicien — c'est de systématiser l'exploration de l'espace de design, de faire remonter les compromis non évidents, et de fournir un raisonnement transparent et reproductible à chaque étape. Comme disait Feynman : "What I cannot create, I do not understand." Ici, on crée les candidats ET on les comprend.
Toute la physique du projet tient dans ces équations. On ne triche pas, on ne simplifie pas à outrance. Dirac serait fier — ou au moins pas mécontent.
Le cœur de CryoSwarm-Q, c'est le Hamiltonien driven Rydberg pour
avec :
| Symbole | Signification | Plage typique |
|---|---|---|
| Fréquence de Rabi (amplitude du drive) | 1 – 15 rad/μs | |
| Désaccord (offset énergétique de l'état Rydberg) | −40 à +25 rad/μs | |
| Opérateur nombre Rydberg pour l'atome |
— | |
| Coefficient de Van der Waals |
|
|
| Position de l'atome |
Espacement : 4 – 15 μm |
L'espèce atomique est le
Quand deux atomes sont plus proches que le rayon de blocage
À
L'état fondamental du Hamiltonien Rydberg en régime de blocage (grand
CryoSwarm-Q calcule le MIS exact pour
La couche de simulation calcule ces observables à partir de la fonction d'onde de l'état fondamental
| Observable | Formule | Signification physique |
|---|---|---|
| Densité Rydberg | Probabilité d'excitation de l'atome |
|
| Fraction Rydberg totale | Excitation moyenne sur le registre | |
| Corrélation connectée | Corrélations quantiques au-delà du champ moyen | |
| Ordre antiferromagnétique | Magnétisation alternée (1 = ordre de Néel parfait) | |
| Entropie d'intrication | Intrication bipartite via décomposition de Schmidt | |
| Probabilité de bitstring | Probabilité de mesurer chaque état de la base computationnelle |
CryoSwarm-Q génère des schedules de pulses à partir de 5 familles — chacune a sa personnalité, comme les 5 postulats de la mécanique quantique (sauf que ceux-là marchent du premier coup) :
| Famille | Cas d'usage | ||
|---|---|---|---|
constant_drive |
Constante |
Constante |
Oscillation de Rabi simple |
global_ramp |
Rampe linéaire |
Balayage linéaire | Excitation progressive |
detuning_scan |
Constante |
Balayage linéaire | Scan de résonance |
adiabatic_sweep |
Enveloppe |
Balayage linéaire | Préparation d'état adiabatique |
blackman_sweep |
Fenêtre de Blackman | Balayage linéaire | Préparation adiabatique basse fuite |
L'évolution temporelle suit
Chaque candidat reçoit un score objectif composite — parce qu'un seul nombre ne raconte jamais toute l'histoire (Heisenberg l'avait compris avant tout le monde) :
avec les poids par défaut
Le scoring de robustesse agrège simulations nominales et perturbées :
avec les poids
Le bonus de stabilité récompense la faible variance entre les scénarios de bruit, tandis qu'une pénalité signale les candidats dont la performance chute brutalement entre le nominal et le pire cas.
Trois scénarios de perturbation sont appliqués à chaque candidat — parce qu'un bon candidat, c'est celui qui tient la route même quand Laplace arrête de croire au déterminisme :
| Scénario |
|
SPAM | Spatial | ||||
|---|---|---|---|---|---|---|---|
| BAS | 0.01 | 0.01 | 0.001 | 0.001 | 50 | 0.5% | 2% |
| MOYEN | 0.05 | 0.05 | 0.005 | 0.005 | 50 | 0.5% | 5% |
| STRESSÉ | 0.10 | 0.10 | 0.010 | 0.010 | 50 | 0.5% | 8% |
Les canaux de bruit incluent : fluctuation d'amplitude ($\Omega \to \Omega(1+\epsilon_\Omega)$), dérive du désaccord, déphasage Lindblad, perte d'atomes, erreurs SPAM, et inhomogénéité spatiale du drive.
Comme disait von Neumann : "Avec quatre paramètres je peux modéliser un éléphant, avec cinq je le fais bouger la trompe." Ici on a 8 agents, et chacun sait exactement quoi faire.
CryoSwarm-Q repose sur des agents spécialisés avec des responsabilités explicites, orchestrés dans un pipeline déterministe :
ObjectifExpérimental
│
▼
┌─────────────────┐
│ ProblemFraming │ Objectif → SpécificationExpérience
│ Agent │ (nb atomes, géométrie, observable cible)
└────────┬────────┘
▼
┌─────────────────┐
│ GeometryAgent │ Propose des registres faisables hardware
│ │ (espacement, blocage, bornes du device)
└────────┬────────┘
▼
┌─────────────────┐
│ SequenceStrategy │ Génère les candidats pulse
│ (heuristique/RL/ │ (5 familles × variations de paramètres)
│ hybride/bandit) │
└────────┬────────┘
▼
┌─────────────────┐
│ SurrogateFilter │ Pré-filtre les candidats (optionnel)
│ │ (prédiction d'ensemble + incertitude)
└────────┬────────┘
▼
┌─────────────────┐
│ NoiseRobustness │ Évalue nominal + 3 scénarios de bruit
│ Agent │ (évaluation parallèle supportée)
└────────┬────────┘
▼
┌─────────────────┐
│ CampaignAgent │ Classe les candidats par score composite
│ │ (lexicographique : obj > worst > robust > nom)
└────────┬────────┘
▼
┌─────────────────┐
│ MemoryAgent │ Extrait et stocke les leçons réutilisables
│ │ (taggées par classe de problème)
└────────┬────────┘
▼
RésuméPipeline
Chaque agent produit une AgentDecision avec sortie structurée, raisonnement et horodatage — traçabilité totale.
Le SequenceStrategy sélectionne comment les candidats pulse sont générés :
| Mode | Description |
|---|---|
heuristic_only |
Balayages de paramètres sur les 5 familles de formes d'onde |
rl_only |
La politique PPO propose les candidats directement |
hybrid |
Évalue les candidats heuristiques et RL ensemble |
adaptive |
Change de stratégie par classe de problème selon l'historique |
bandit |
Bandit multi-bras UCB1 sélectionne la meilleure stratégie |
Le ML ici, c'est pas du fine-tuning de LLM sur des tweets. C'est de l'apprentissage qui sert la physique — Planck aurait approuvé (après avoir râlé sur les constantes).
Trois réseaux SurrogateModelV2 entraînés indépendamment prédisent les scores de robustesse sans simulation complète — un raccourci honnête :
Input(18) → Linear(128) → GELU →
[ResidualBlock(LayerNorm → Linear → GELU → Dropout → Linear)] × 3
→ LayerNorm → Linear(64) → GELU → Linear(4) → Sigmoid
L'entrée (18 dimensions) est un vecteur de features physiquement motivé (nombre d'atomes, ratios d'espacement, métriques de blocage, paramètres de pulse). Les 4 sorties sont (robustness_score, nominal_score, worst_case_score, observable_score).
L'incertitude épistémique est estimée par la variance inter-modèles :
Les candidats à forte incertitude sont priorisés pour re-simulation (apprentissage actif).
Un réseau ActorCritic apprend à proposer des paramètres de pulse directement — pas d'heuristique, le réseau fait ses propres choix :
- Observation : vecteur 16-dim (nombre d'atomes, espacement, rayon de blocage, faisabilité, densité cible, meilleure robustesse jusqu'ici, meilleurs paramètres, progression de l'épisode)
-
Action : 4-dim continu
$[-1, 1]$ remis à l'échelle vers (amplitude, désaccord, durée, famille) - Objectif : PPO clippé avec estimation d'avantage GAE
Le système itère entre entraînement du surrogate et entraînement RL — comme un chercheur qui alterne entre théorie et expérience, sauf qu'il ne dort jamais :
- Entraîner / affiner l'ensemble surrogate sur les données de simulation
- Entraîner PPO en utilisant les prédictions surrogate comme proxy de récompense rapide
- Collecter les configurations RL les plus fortes
- Sélectionner des points diversifiés à forte incertitude
- Re-simuler avec l'évaluateur réel
- Enrichir le dataset et recommencer
Un bandit multi-bras UCB1 suit la performance par stratégie au fil des campagnes :
Ça balance automatiquement l'exploration de nouvelles stratégies et l'exploitation des bonnes connues.
8 pages, chacune dédiée à une étape du pipeline. Tu vois tout, tu comprends tout, tu peux tout inspecter. Galilée aurait adoré — lui qui insistait pour que tout soit observable.
Lancement :
streamlit run apps/dashboard/app.pyLa page opérationnelle principale. Lance de nouvelles campagnes en spécifiant titre, objectif scientifique, nombre d'atomes (2–50), géométrie préférée, observable cible et priorité. Pendant l'exécution, un bus d'événements propage les décisions agents en temps réel.
Après exécution :
- Campagnes récentes — tableau des 20 dernières campagnes
- Inspecteur de pipeline — indicateurs de phase par agent + diagramme de Gantt
- Entonnoir de candidats — visualisation (Registres → Séquences → Évalués → Classés)
- Log de décisions agents — sorties structurées dépliables pour chaque appel
Exploration de la physique des géométries de registre :
- Scatter 2D des positions atomiques avec cercles de rayon de blocage (colorés par densité Rydberg quand disponible)
-
Heatmap d'interactions Van der Waals — interactions
$C_6 / r_{ij}^6$ par paires - Graphe de blocage avec les MIS surlignés
- Histogramme des distances avec le seuil de blocage marqué
-
Slider
$\Omega$ interactif qui recalcule$R_b$ dynamiquement - Métriques : nombre d'atomes, distance minimale, paires en blocage, score de faisabilité
Diagonalisation en temps réel et analyse spectrale (jusqu'à 14 atomes) :
- Spectre d'énergie des 20 premiers niveaux
- Distribution de probabilité de bitstring avec les bitstrings MIS en or
-
Gap spectral (
$\Delta E = E_1 - E_0$ ), dimension de Hilbert ($2^N$ ), Inverse Participation Ratio - Composition d'état : fraction Rydberg, entropie d'intrication, paramètre d'ordre AF
-
Balayage paramétrique en désaccord révélant croisements évités et transitions de phase (pour
$N \leq 10$ ) - Chargement auto des contrôles depuis le meilleur candidat
Inspection et design des formes d'onde :
-
Visualisation des formes d'onde (
$\Omega(t)$ et$\delta(t)$ dans le temps) pour chaque séquence candidate - Panneau mathématiques — formules Blackman, balayage linéaire, Trotter-Suzuki
- Table de comparaison entre familles, amplitudes, désaccords, durées et coûts prédits
- Scatter de l'espace des paramètres (séquences colorées par score objectif)
-
Générateur de formes d'onde — design libre (famille,
$\Omega_{\max}$ ,$\delta_{\text{start}}$ ,$\delta_{\text{end}}$ , durée)
Analyse de sensibilité au bruit et comparaison des scores — le stress-test des candidats, comme un concours de prépa mais pour des atomes :
- Graphe barres groupées — scores nominal, moyenne des perturbations et pire cas
- Radar chart de bruit — comparaison multi-candidats (jusqu'à 4)
- Violin plot de robustesse pour la distribution des scores
- Deep dive par candidat : cascade de dégradation + table d'observables (nominal vs BAS/MOYEN/STRESSÉ)
- Métriques : score nominal, moyenne perturbée, pire cas, écart-type, pénalité de robustesse
- Export du rapport complet (JSON)
Monitoring du sous-système ML en 4 onglets :
- Modèle Surrogate — courbes de loss, résumé d'architecture (18→128→GELU→3 blocs résiduels→4), statut des checkpoints
- Entraînement PPO — courbes de récompense, loss policy, loss valeur, entropie, hyperparamètres, description des espaces action/observation
- Bandit de stratégie — évolution UCB1, pie chart de distribution, statistiques par stratégie
- RL vs Heuristique — barres groupées RL vs heuristique par trial
Analyse de tendances inter-campagnes :
- Timeline (jusqu'à 50 campagnes)
- Évolution des scores en ligne
- Distribution des backends en barres empilées
- Explorateur 3D de l'espace des paramètres avec front de Pareto
- Système de mémoire — nuage de tags, filtres par type de leçon/campagne/tag, JSON dépliable
- Stats : total campagnes, candidats moyens par campagne, meilleur score historique, backend favori, taux de succès
Référence mathématique autonome en 8 sections dépliables :
- Hamiltonien Rydberg — Hamiltonien complet, gap spectral, IPR, condition adiabatique
- Observables quantiques — densité Rydberg, corrélations, ordre AF, entropie d'intrication, fidélité
- Maximum Independent Set — définition MIS, mesure d'overlap, fonction de coût
- Familles de séquences de pulses — 5 familles avec table de profils
- Scoring de robustesse — formule, poids, bonus stabilité, pénalité, paramètres de bruit
- Score objectif et classement — scoring composite, clé de classement lexicographique
- PPO et Apprentissage par renforcement — objectif PPO, ratio d'importance, GAE, détails de l'environnement
- Ensemble surrogate et incertitude — architecture, incertitude épistémique, stratégie UCB1
Chaque section renvoie vers le fichier d'implémentation correspondant.
CryoSwarm-Q expose un backend FastAPI pour l'accès programmatique — parce que les vrais utilisent des endpoints, pas des boutons :
uvicorn apps.api.main:app --reloadURL de base : /api/v1/
| Endpoint | Méthode | Description |
|---|---|---|
/api/v1/health |
GET | Health check et statut de connectivité MongoDB |
/api/v1/campaigns/ |
POST | Lancer une nouvelle campagne depuis un objectif |
/api/v1/campaigns/ |
GET | Lister les campagnes (avec pagination) |
/api/v1/campaigns/{id} |
GET | Détail complet d'une campagne |
/api/v1/candidates/{campaign_id} |
GET | Candidats classés pour une campagne |
- Python 3.10+
- MongoDB (optionnel — le système tombe automatiquement sur du stockage en mémoire)
pip install -e ".[dev]"Ou installer les dépendances directement :
pip install -r requirements.txt| Package | Rôle |
|---|---|
pulser, pulser-simulation |
Construction native de séquences de pulses Pasqal |
pasqal-cloud |
Adaptateur d'exécution cloud |
qoolqit |
Toolkit d'optimisation quantique |
torch |
Modules ML (surrogate, PPO, apprentissage actif) |
pytest tests -qstreamlit run apps/dashboard/app.pyuvicorn apps.api.main:app --reloadpython -m scripts.run_demo_pipelineGénérer un dataset d'entraînement :
python -m scripts.train_ml --phase generate_v2 --n-samples 1000 --workers 2 --sampling lhsEntraîner l'ensemble surrogate :
python -m scripts.train_ml --phase surrogate --data data/generated/dataset.npz --epochs 100Entraîner PPO :
python -m scripts.train_ml --phase rl --updates 500Entraînement complet (surrogate + PPO) :
python -m scripts.train_ml --phase full --data data/generated/dataset.npz --epochs 100 --updates 500Boucle d'apprentissage actif :
python -m scripts.train_ml --phase active --data data/generated/dataset.npz --al-iterations 5 --al-top-k 200Benchmarks et ablations :
python -m scripts.benchmark --full --checkpoint-dir checkpoints --test-data data/generated/dataset.npz
python -m scripts.ablation --ablation all --data data/generated/dataset.npzfrom packages.core.models import ExperimentGoal
from packages.orchestration.pipeline import CryoSwarmPipeline
goal = ExperimentGoal(
title="Balayage robuste atomes neutres",
scientific_objective="Chercher des protocoles de densité Rydberg robustes.",
target_observable="rydberg_density",
desired_atom_count=6,
preferred_geometry="mixed",
)
pipeline = CryoSwarmPipeline(
sequence_strategy_mode="adaptive",
rl_checkpoint_path="checkpoints/ppo_latest.pt",
)
summary = pipeline.run(goal)
print(summary.status, summary.top_candidate_id)Le pipeline retourne un PipelineSummary contenant l'état de la campagne, tous les candidats classés, les décisions agents, les rapports de robustesse et les enregistrements mémoire.
372 tests. Pas un de trop, pas un de moins. Rutherford testait ses atomes un par un — nous on fait pareil mais plus vite.
Le repo maintient 372 tests couvrant :
| Domaine | Couverture |
|---|---|
| Construction du Hamiltonien (dense + sparse) | Exactitude physique, valeurs propres, symétrie |
| Observables quantiques | Densité Rydberg, corrélations, intrication |
| Agent géométrie | Contraintes d'espacement, conditions de blocage, bornes device |
| Agent séquence | 5 familles de formes d'onde, validation de paramètres |
| Profils de bruit | 3 scénarios, application des perturbations |
| Scoring de robustesse | Formule d'agrégation, bonus stabilité, pénalité |
| Scoring objectif | Score composite, normalisation des poids |
| Intégration pipeline | Exécution de campagne bout en bout |
| Pipeline parallèle | Évaluation de bruit concurrente |
| Endpoints API | Health check, CRUD campagnes, gestion d'erreurs |
| Modèle surrogate | Entraînement, inférence, incertitude d'ensemble |
| PPO + environnement RL | Espace d'action, observation, shaping de récompense |
| Dataset ML | Génération de features, normalisation |
| Routage backend | Logique de routage émulateur |
| Agent mémoire | Extraction de leçons, génération de tags |
Lancer avec couverture :
pytest tests -q --tb=shortLe plan de la base. Chaque dossier a un rôle précis — pas de fourre-tout, pas de mystère. Leibniz voulait un langage universel pour les sciences — on a fait mieux : une arborescence bien rangée.
packages/ Le cœur du système
├── core/ Le socle — modèles de données, config, constantes
├── agents/ 8 agents spécialisés + switching de stratégie
├── orchestration/ Pipeline, phases, bus d'événements
├── simulation/ Hamiltonien, observables, bruit, cache
├── scoring/ Scoring de robustesse + objectif, classement
├── ml/ Surrogate, PPO, env RL, apprentissage actif
├── pasqal_adapters/ Traducteurs vers Pulser, Pasqal Cloud, QoolQit
└── db/ Persistance MongoDB
apps/
├── api/ API REST FastAPI (5 routes + WebSocket)
└── dashboard/ Dashboard Streamlit (9 pages)
scripts/ CLI : entraînement, benchmarks, démo
configs/ Hyperparamètres YAML
data/ Datasets générés
checkpoints/ Modèles entraînés (surrogate, PPO)
experiments/ Tracking des runs d'entraînement
tests/ 372 tests
Cette section, c'est le mode "capot ouvert". On t'explique chaque pièce du moteur, à quoi elle sert, et quel type de programme c'est. Comme disait Euler : "Rien n'est plus pratique qu'une bonne théorie" — et ici, rien n'est plus clair qu'une bonne cartographie.
Tout le monde importe depuis core. C'est les fondations de la maison — si c'est bancal ici, tout s'effondre.
| Fichier | Type | Ce qu'il fait |
|---|---|---|
models.py |
Modèles de données Pydantic | Définit TOUS les objets du système : ExperimentGoal, ExperimentSpec, RegisterCandidate, SequenceCandidate, RobustnessReport, EvaluationResult, CampaignState, MemoryRecord, AgentDecision... C'est le dictionnaire du projet — si tu veux comprendre une donnée, c'est ici. |
enums.py |
Constantes énumérées | Tous les statuts (GoalStatus, CampaignStatus), les backends (BackendType), les familles de séquences (SequenceFamily), les niveaux de bruit (NoiseLevel), les noms d'agents (AgentName). Ça évite les strings magiques partout. |
config.py |
Configuration applicative | Gère les settings avec Pydantic : URI MongoDB, clé API, credentials Pasqal, CORS, rate limiting. Détecte l'environnement (dev/test/prod). |
parameter_space.py |
Espace des paramètres physiques | Centralise TOUS les paramètres tunables : nombre d'atomes (2–50), espacement (4–15 μm), amplitude (1–15 rad/μs), désaccord (-40 à +25 rad/μs), coefficient |
training_config.py |
Config ML | Charge les hyperparamètres depuis le YAML et dérive les échelles de normalisation des features à partir de l'espace physique. |
exceptions.py |
Hiérarchie d'exceptions | Exceptions typées par domaine : AgentError, GeometryError, SequenceError, PipelineError... Chaque erreur porte le nom de l'agent responsable. |
metadata_schemas.py |
Contrats de données (TypedDict) | Schémas typés pour les métadonnées échangées entre agents : RegisterMetadata, SequenceMetadata, EvaluationMetadata, MemorySignals. |
logging.py |
Logging | Configuration du logger singleton. On sait tout ce qui se passe, quand, et pourquoi. |
Chaque agent fait UN truc et le fait bien. C'est le principe de responsabilité unique — Uncle Bob serait content, et Turing aussi d'ailleurs.
| Fichier | Type | Ce qu'il fait |
|---|---|---|
base.py |
Classe abstraite | BaseAgent — la classe mère de tous les agents. Fournit le logger et la méthode build_decision() qui produit des AgentDecision traçables (horodatées, avec raisonnement). |
protocols.py |
Interfaces (Protocol) | Contrats runtime-checkable : ProblemFramingProtocol, GeometryProtocol, SequenceProtocol, etc. C'est ce qui garantit que chaque agent respecte son API sans héritage rigide. |
problem_agent.py |
Agent de cadrage | ProblemFramingAgent — prend un ExperimentGoal ("je veux étudier la densité Rydberg sur 6 atomes") et le transforme en ExperimentSpec (nombre d'atomes exact, géométrie, familles de séquences à explorer, densité cible). Utilise la mémoire pour biaiser le choix de backend. |
geometry_agent.py |
Agent de géométrie | GeometryAgent — génère des registres atomiques faisables hardware. Sait créer des grilles carrées, des lignes, des triangulaires, des anneaux, des zigzags, des nids d'abeille. Vérifie les contraintes : espacement min, rayon de blocage, bornes du device. Calcule la matrice de Van der Waals. |
sequence_agent.py |
Agent de séquences | SequenceAgent — génère des candidats pulse par heuristiques. Balaye les 5 familles × variations de paramètres. Prédit le coût d'exécution. C'est la version "force brute intelligente" de la génération. |
noise_agent.py |
Agent de robustesse bruit | NoiseRobustnessAgent — évalue chaque candidat sous bruit. Lance la simulation nominale + 3 scénarios perturbés (BAS/MOYEN/STRESSÉ). Produit un RobustnessReport avec scores nominal, moyen, pire cas. |
routing_agent.py |
Agent de routage | BackendRoutingAgent — décide quel simulateur utiliser. Petits systèmes (≤8 atomes) → EMU_SV (state vector exact). Moyens (≤16 atomes) → EMU_MPS (tensor network approché). Sinon → LOCAL_PULSER. |
campaign_agent.py |
Agent de campagne | CampaignAgent — classe tous les candidats évalués par score composite (lexicographique : objectif > pire cas > robustesse > nominal). Sélectionne le meilleur, met à jour le statut de la campagne. |
memory_agent.py |
Agent de mémoire | MemoryAgent — après chaque campagne, extrait les top 3 candidats et crée des MemoryRecord taggés. Stocke les signaux : scores, backend, famille, layout, confiance, gap spectral. Ces leçons alimentent les futures campagnes. |
results_agent.py |
Agent de résultats | ResultsAgent — génère le rapport final : résumé de l'objectif, de la spec, du meilleur candidat, du backend recommandé, des scores. |
sequence_strategy.py |
Sélecteur de stratégie | SequenceStrategy — décide COMMENT générer les séquences. 5 modes : HEURISTIC_ONLY, RL_ONLY, HYBRID, ADAPTIVE, BANDIT. Le mode bandit utilise UCB1 pour explorer/exploiter les stratégies automatiquement. |
C'est lui qui dit "toi tu fais ça, toi tu passes après, toi tu notes". Sans orchestration, les agents seraient des musiciens qui jouent chacun dans leur coin.
| Fichier | Type | Ce qu'il fait |
|---|---|---|
pipeline.py |
Pipeline principal | CryoSwarmPipeline — LE point d'entrée. Crée le contexte, enchaîne les phases dans l'ordre, gère la parallélisation de l'évaluation bruit, propage le contexte entre les phases. C'est la méthode run() qui lance tout. |
phases.py |
Phases composables | 9 classes de phase : ProblemFramingPhase, GeometryGenerationPhase, SequenceGenerationPhase, SurrogateFilterPhase, EvaluationPhase, RankingPhase, MemoryCapturePhase, ResultsSummaryPhase. Chaque phase opère sur un PipelineContext mutable et publie des événements. |
events.py |
Bus d'événements | EventBus — système pub/sub in-process. Chaque phase publie des PipelineEvent (début, fin, erreur). Le dashboard s'y abonne pour le monitoring en temps réel. Supporte le replay et les abonnements par campagne. |
runner.py |
Lanceur de démo | run_demo_campaign() — raccourci pour lancer une campagne démo complète avec un objectif prédéfini. |
C'est le labo. Ici on diagonalise des Hamiltoniens, on calcule des observables, on simule du bruit. Schrödinger ferait tourner ce code sur son chat (s'il en avait un).
| Fichier | Type | Ce qu'il fait |
|---|---|---|
hamiltonian.py |
Construction du Hamiltonien | Construit la matrice Hamiltonienne Rydberg dense ( |
observables.py |
Calcul des observables | À partir du vecteur d'état $ |
evaluators.py |
Moteur d'évaluation |
evaluate_candidate_robustness() — orchestre l'évaluation complète d'un candidat. Lance la simulation Pulser QutipEmulator (ou le backend numpy) en nominal + perturbé. Calcule les observables et les métriques Hamiltoniennes. Met en cache les résultats. |
noise_profiles.py |
Profils de bruit | Trois scénarios prédéfinis : low_noise() (medium_noise() (stressed_noise() ( |
numpy_backend.py |
Simulateur exact NumPy | Backend de simulation indépendant de Pulser. Implémente l'évolution temporelle par splitting de Strang (Trotter-Suzuki ordre 2). Définit un PulseSchedule pour décrire les formes d'onde. Fallback quand Pulser n'est pas installé. |
evaluation_cache.py |
Cache de simulation | Cache LRU adressable par contenu. Hash la spec, le registre, la séquence et le scénario de bruit en une signature sémantique. Évite de re-simuler ce qu'on a déjà calculé. |
3 fichiers, 3 responsabilités. Pas de bavardage — ici on note et on classe.
| Fichier | Type | Ce qu'il fait |
|---|---|---|
robustness.py |
Score de robustesse | Calcule l'agrégat : $S_{robust} = 0.25 \cdot s_{nom} + 0.35 \cdot \bar{s}{pert} + 0.30 \cdot s{worst} + 0.10 \cdot b_{stab}$. Bonus de stabilité si la variance est faible. Pénalité si le pire cas dégringole par rapport au nominal. |
objective.py |
Score objectif composite |
|
ranking.py |
Classement | Tri lexicographique : score objectif > pire cas > robustesse > nominal. Assigne un rang final et le statut RANKED à chaque candidat. |
C'est ici que vit le PPO, le surrogate, l'apprentissage actif. Pour ceux qui cherchent "mais il est où le réseau de neurones ?" — c'est là.
| Fichier | Type | Ce qu'il fait |
|---|---|---|
surrogate.py |
Réseau de neurones PyTorch | SurrogateModel — MLP avec 3 blocs résiduels (LayerNorm → Linear → GELU → Dropout → Linear). Entrée : 18 features physiques. Sortie : 4 scores (robustesse, nominal, pire cas, observable). SurrogateEnsemble — 3 modèles indépendants dont on utilise la variance comme mesure d'incertitude. |
dataset.py |
Préparation des données | build_feature_vector_v2() — transforme un registre + séquence en vecteur 18-dim (nombre d'atomes, ratios d'espacement, métriques de blocage, paramètres de pulse, encodage layout/famille). MLDataset — PyTorch Dataset standard. |
data_generator.py |
Génération de datasets | DataGenerator — génère des milliers de configurations par échantillonnage Latin Hypercube / Sobol, les simule, et stocke les résultats en .npz. C'est ce qui produit les données d'entraînement pour le surrogate. |
normalizer.py |
Normalisation | DatasetNormalizer — fit mean/std sur les données d'entraînement, transform/inverse_transform, sérialise en .npz. |
surrogate_filter.py |
Filtre pré-simulation | SurrogateFilter — utilise l'ensemble surrogate pour pré-trier les candidats AVANT la simulation complète. Garde les top-k par score prédit, exclut ceux avec trop d'incertitude. |
| Fichier | Type | Ce qu'il fait |
|---|---|---|
ppo.py |
🎯 L'algo PPO (Proximal Policy Optimization) | C'est ICI que vit le PPO. ActorCritic — réseau PyTorch avec deux têtes : l'acteur (propose des actions = paramètres de pulse) et le critique (estime la valeur de l'état = "est-ce que cette situation est bonne ?"). PPOTrainer — implémente l'algorithme PPO : collecte des rollouts, calcule l'avantage GAE (Generalized Advantage Estimation), optimise avec le loss clippé |
rl_env.py |
Environnement RL (style Gymnasium) |
PulseDesignEnv — l'environnement dans lequel le PPO apprend. Observation (16-dim) : nombre d'atomes, espacement, rayon de blocage, faisabilité, densité cible, meilleure robustesse vue, meilleurs paramètres, progression. Action (4-dim continu) : amplitude Ω, désaccord δ, durée T, choix de famille de pulse. Récompense : le score objectif du candidat généré. C'est la boucle observation → action → récompense → apprentissage. |
rl_sequence_agent.py |
Agent RL pour les séquences |
RLSequenceAgent — wrapper qui prend la politique PPO entraînée et l'utilise comme générateur de séquences dans le pipeline. Si le PPO n'est pas entraîné → fallback automatique sur l'heuristique. |
curriculum.py |
Curriculum learning |
CurriculumScheduler — entraîne le PPO progressivement : d'abord sur des petits systèmes (3 atomes), puis moyens (8), puis grands (16). Comme un prof qui commence par les exos faciles. Seuils de performance minimaux pour passer au niveau suivant. |
| Fichier | Type | Ce qu'il fait |
|---|---|---|
active_learning.py |
Boucle d'apprentissage actif | ActiveLearningLoop — le cycle complet : (1) entraîner le surrogate, (2) entraîner le PPO avec le surrogate comme proxy de récompense, (3) identifier les points à forte incertitude (là où le surrogate doute), (4) simuler ces points pour de vrai, (5) enrichir le dataset, (6) recommencer. C'est comme un étudiant qui révise uniquement ce qu'il ne maîtrise pas. |
| Fichier | Type | Ce qu'il fait |
|---|---|---|
experiment_tracking.py |
Tracking CSV | ExperimentTracker — log chaque run d'entraînement : commit git, device, dataset, métriques par step (loss, reward), registre d'artefacts. |
training_runner.py |
Orchestrateur d'entraînement | train_surrogate(), train_rl(), train_active_learning() — charge les données, gère les checkpoints, lance le curriculum. |
gpu_backend.py |
Accélération GPU | Construction du Hamiltonien sparse sur GPU (PyTorch). Approximation de Krylov pour l'évolution temporelle. Détection automatique : CUDA / ROCm (AMD MI300X) / MPS (Apple) / CPU. |
CryoSwarm-Q parle son propre langage en interne. Ces adaptateurs traduisent vers l'écosystème Pasqal — comme des interprètes entre deux pays.
| Fichier | Type | Ce qu'il fait |
|---|---|---|
pulser_adapter.py |
Adaptateur Pulser | Convertit les RegisterCandidate et SequenceCandidate internes en objets Pulser natifs (Register, Sequence). Gère les 5 familles de formes d'onde (Ramp, Blackman, Constant), clippe les amplitudes pour marge de sécurité, quantise la durée sur la période d'horloge. |
emulator_router.py |
Routeur d'émulateur | recommend_backend() — décide quel simulateur utiliser en fonction de la taille du système et du profil de robustesse. ≤8 atomes → state vector exact (EMU_SV). ≤16 atomes → tensor network approché (EMU_MPS). Sinon → Pulser local. |
pasqal_cloud_adapter.py |
Adaptateur Pasqal Cloud | Interface vers le SDK cloud Pasqal. Authentification, soumission de batches, vérification de statut. Dégradation gracieuse si les credentials sont absents. |
qoolqit_adapter.py |
Bridge QoolQit | Encode le graphe d'interaction de blocage en QUBO (Quadratic Unconstrained Binary Optimization) pour résolution MIS via QoolQit. |
| Fichier | Type | Ce qu'il fait |
|---|---|---|
mongodb.py |
Client MongoDB | Connexion singleton thread-safe avec pool management, timeouts, config de taille de pool. |
repositories.py |
Couche Repository | CryoSwarmRepository — pattern Repository avec CRUD complet pour tous les modèles : create_goal(), get_campaign(), list_candidates_for_campaign(), store_memory_record(), etc. |
init_db.py |
Initialisation | Crée les collections et les index (unique sur id, index sur campaign_id, goal_id, timestamps) au premier démarrage. |
Pour ceux qui veulent interagir avec CryoSwarm-Q sans le dashboard — ou qui construisent un frontend custom.
| Fichier | Type | Ce qu'il fait |
|---|---|---|
main.py |
Application FastAPI | Factory : lifespan (init MongoDB au démarrage, close au shutdown), middleware CORS, rate limiting, broadcast d'événements. |
auth.py |
Authentification | Vérification API key (constant-time) via header X-API-Key. Optionnel si pas de clé configurée. |
dependencies.py |
Injection de dépendances | Fournit le CryoSwarmRepository injecté dans chaque route qui en a besoin. |
rate_limit.py |
Rate limiting | Limiteur de requêtes par fenêtre fixe. Track par client, retourne le quota restant et le temps de retry. |
live.py |
Broadcast temps réel | CampaignEventBroadcaster — pousse les événements pipeline vers les clients WebSocket par campaign_id. |
routes/health.py |
GET /health | Statut de l'app, de l'environnement, de la connectivité MongoDB. |
routes/goals.py |
POST/GET /goals | Création et récupération d'objectifs expérimentaux. |
routes/campaigns.py |
POST/GET /campaigns | Lancement de campagnes démo, récupération de l'état complet d'une campagne. |
routes/candidates.py |
GET /candidates | Liste des candidats évalués et classés pour une campagne donnée. |
routes/streaming.py |
WebSocket /ws/campaigns | Streaming live des événements pipeline vers le navigateur. |
| Fichier | Ce qu'il fait |
|---|---|
app.py |
Page d'accueil : sidebar avec liste des campagnes, métriques globales, liens vers les sous-pages. |
logic.py |
Helpers Python purs (testables sans Streamlit) : formatage des tables, calcul de Pareto, résumé d'événements. |
components/data_loaders.py |
Chargement des données depuis MongoDB ou le repo. |
components/latex_panels.py |
7 générateurs de panneaux LaTeX : formules du Hamiltonien, observables, MIS, pulses, robustesse, ML, campagne. |
components/plotly_charts.py |
20+ fonctions de visualisation Plotly : spectres, heatmaps, barres, radar, violins, 3D. |
pages/1_Campaign_Control.py |
Lancer et monitorer des campagnes en temps réel. |
pages/2_Register_Physics.py |
Explorer la physique des registres : scatter 2D, heatmap VdW, graphe de blocage. |
pages/3_Hamiltonian_Lab.py |
Spectroscopie : diagonalisation, spectre d'énergie, gap spectral, IPR. |
pages/4_Pulse_Studio.py |
Design de séquences : visualisation des formes d'onde, comparaison, générateur. |
pages/5_Robustness_Arena.py |
Analyse de robustesse : barres groupées, radar, violin, cascade de dégradation. |
pages/6_ML_Observatory.py |
Monitoring ML : loss surrogate, reward PPO, bandit UCB1, RL vs heuristique. |
pages/7_Campaign_Analytics.py |
Tendances inter-campagnes, front de Pareto 3D, nuage de tags mémoire. |
pages/8_Theory_Reference.py |
Référence mathématique complète (300+ lignes) : 8 sections dépliables. |
pages/9_Training_Tracker.py |
Suivi des runs d'entraînement : métriques, courbes, observations. |
| Fichier | Ce qu'il fait |
|---|---|
train_ml.py |
CLI d'entraînement ML — 6 phases : generate (créer des données), generate_v2 (dataset LHS grande échelle), surrogate (entraîner le surrogate ~200 epochs), rl (PPO ~500 updates), active (boucle d'apprentissage actif), full (surrogate + PPO bout en bout). |
benchmark.py |
Suite de benchmarks — MSE/MAE/R² pour le surrogate, récompense/convergence pour le PPO, robustesse moyenne pour le pipeline. Inclut hash git et timestamps. |
ablation.py |
Études d'ablation — 9 configurations testées : heuristique pure, surrogate filter v1/v2, RL single/multi-step, curriculum, hybride, ensemble 3 modèles, pipeline complet. Compare pour voir ce qui marche vraiment. |
run_demo_pipeline.py |
Démo rapide — lance une campagne complète et affiche le meilleur candidat. |
seed_demo_goal.py |
Seed d'objectif — crée un objectif test en base pour les tests de la chaîne. |
┌──────────────────────────────────────────────────────────┐
│ DASHBOARD Streamlit (9 pages) │
│ → L'interface visuelle pour tout inspecter │
└──────────────────────┬───────────────────────────────────┘
│ HTTP / WebSocket
┌──────────────────────▼───────────────────────────────────┐
│ API FastAPI (5 routes + WebSocket live) │
│ → Le point d'entrée programmatique │
└──────────────────────┬───────────────────────────────────┘
│
┌──────────────────────▼───────────────────────────────────┐
│ ORCHESTRATION (Pipeline + 9 Phases + EventBus) │
│ → Le chef d'orchestre qui enchaîne les agents │
│ │
│ Cadrage → Géométrie → Séquences → Filtre surrogate │
│ → Évaluation bruit → Classement → Mémoire → Résultats │
└───┬──────────────┬──────────────┬────────────────────────┘
│ │ │
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌───────────────┐
│SIMULA- │ │ SCORING │ │ ML PIPELINE │
│TION │ │ │ │ │
│ │ │objective │ │surrogate.py │
│hamilto-│ │robustness│ │ → 3 réseaux │
│nian.py │ │ranking │ │ppo.py │
│observa-│ │ │ │ → PPO actor- │
│bles.py │ │ │ │ critic │
│noise │ │ │ │rl_env.py │
│evaluat-│ │ │ │ → environnmt │
│ors │ │ │ │active_learn. │
│cache │ │ │ │ → boucle AL │
└───┬────┘ └──────────┘ └───────┬───────┘
│ │
▼ ▼
┌──────────────────────────────────────────┐
│ PASQAL ADAPTERS │
│ pulser_adapter → Pulser natif │
│ emulator_router → EMU_SV / EMU_MPS │
│ pasqal_cloud → SDK Cloud │
│ qoolqit_adapter → QUBO / MIS │
└──────────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ PERSISTENCE : MongoDB (packages/db/) │
│ → Campagnes, candidats, mémoire, runs │
└──────────────────────────────────────────┘
L'honnêteté intellectuelle, c'est pas optionnel. Voici ce qu'on ne prétend pas faire (pour l'instant).
- Prototype de recherche — pas calibré contre un device physique spécifique.
- Diagonalisation dense du Hamiltonien limitée à
$N \leq 14$ atomes (les méthodes sparse étendent cette plage). - Les modèles surrogate et PPO nécessitent un entraînement avant utilisation ; sans entraînement, le système tombe sur les stratégies heuristiques.
- Les campagnes d'apprentissage actif à grande échelle demandent du compute sérieux (GPU recommandé).
- Les décisions du pipeline sont principées mais pas encore validées contre des données expérimentales.
Prototype de recherche. Consulter le contenu du repository pour les termes du projet.