Aller au contenu

Changelog

Fait par Gillesto — Format Keep a Changelog Versioning : Semantic Versioning 2.0.0

Unreleased

0.1.2 — 2026-10-09

Version corrective issue de l'audit AUDIT.md. Mettre à jour d'urgence : sur 0.1.0 et 0.1.1, compute_routes() échouait systématiquement avec un paquet installé.

Corrigé (critique)

  • Sécurité : PyO3 0.23.5 → 0.29.3. cargo audit (désormais en CI) signalait RUSTSEC-2025-0020 (dépassement de tampon dans PyString::from_object) et RUSTSEC-2026-0177. Le code n'appelait pas la première, mais la dépendance était vulnérable. MSRV Rust : 1.88.
  • S1 — Le moteur Rust n'était jamais chargé dans le paquet installé : from saferoute_core import … alors que maturin place le module dans saferoute.saferoute_core. Import relatif ; message d'erreur clair (NativeModuleNotFoundError) ; en CI (CI=true), l'absence du module natif fait ÉCHOUER la suite au lieu de sauter 3 tests en silence ; test de fumée du wheel dans un environnement vierge (scripts/smoke_wheel.py).
  • S2 — Un seul wheel publié. Le wheel est désormais abi3 (un par système couvre Python ≥ 3.10) ; le workflow construit les wheels Linux (x86_64, aarch64), Windows et macOS (Intel, ARM) avec maturin-action, les teste, puis publie TOUS les fichiers (TestPyPI d'abord, publication de confiance).
  • Recherche non terminante et épuisement mémoire (découvert pendant la mesure, S7). Sur Le Cap, une requête de 1,7 km ne se terminait pas en 90 s puis abattait le processus (memory allocation failed). L'élagage n'utilisait que les coûts déjà payés. La recherche utilise maintenant des bornes inférieures exactes (deux Dijkstra inverses), un élagage ε sur g + h, des labels compacts à pointeurs parents, et un budget de labels (SearchBudgetExceededError, max_labels). Même requête : 84 ms.
  • Boucle infinie sur un cycle d'arcs de coût nul (longueur 0 et risque 0, présents dans OSM) : la dominance stricte ré-insérait indéfiniment le même label. Dominance faible ; test de régression (l'ancien comportement dépasse le délai).
  • Bande passante du KDE fausse (découvert pendant la mesure). bandwidth_m annonçait 400 m alors que le noyau réel faisait 946 à 2 094 m (mesuré) : la carte de risque était sur-lissée. Le noyau a maintenant exactement l'écart-type annoncé, en mètres sur un plan local. Changement de résultat : les scores de risque sont plus localisés qu'en 0.1.1.
  • KDE en O(crimes × arcs) (≈ 10¹¹ évaluations pour Londres, plusieurs heures : cause du chargement « sans fin » de l'audit). Binning + filtre gaussien + interpolation : 2,7 s pour Londres, écart maximal de 0,002 (échelle [0, 1]) avec le calcul exact sur les tests.
  • S3 — CI jamais verte. Chemins SafeRoute/… retirés (le dépôt est à la racine du clone) ; Cargo.lock commité ; extension-module devient une feature (le cœur Rust se teste sans Python) ; cargo fmt et clippy -D warnings (qui échouaient) sont propres ; tests Python sur le paquet installé.
  • S4 — Versions non alignées. 0.1.2 partout (pyproject, Cargo.toml, Cargo.lock) ; __version__ lu depuis les métadonnées du paquet ; bump_version.sh vérifie aussi Cargo.lock. Le tag v0.1.1 n'est pas déplacé (un tag publié ne se réécrit pas) : 0.1.2 le remplace.
  • Dockerfile inutilisable (copiait core/ et bindings/python/, inexistants) : réécrit.
  • Limiteur de débit contournable : l'en-tête X-Forwarded-For, fourni par le client, était cru. Désormais ignoré sauf SAFEROUTE_TRUST_PROXY=true (alors, dernier saut du proxy).
  • Le test test_invalid_node_raises attendait [] mais ne s'exécutait jamais ; il attend maintenant l'erreur explicite (RouteNotFoundError).
  • Familiarité : arcs parallèles — le plus court est retenu (et non le premier de l'itération) ; la mise à jour est exacte par clé (u, v, key).
  • validate_crimes : option deduplicate (la déduplication par coordonnées supprime de vrais crimes).
  • Chargeur Police UK : perte silencieuse de données et interblocage (découverts en téléchargeant 15 mois). Une réponse HTTP 429 était avalée (arrondissement-mois manquant, risque biaisé sans alerte) : nouvelles tentatives avec Retry-After, puis DataSourceError (ou allow_partial=True). Les tâches « > 10 000 crimes » gardaient leur créneau de concurrence en attendant leurs quadrants : cinq mois de Westminster saturaient les cinq créneaux et le téléchargement ne progressait plus.

Modifié

  • S6 — Sérialisation JSON de tout le graphe remplacée par des colonnes numpy remises à Rust en octets (PyGraph.from_arrays) ; record_trip ne reconstruit plus le graphe, il envoie les seuls arcs modifiés (update_familiarity). PyGraph.from_json reste disponible. Le graphe est validé à l'entrée (nœuds uniques, extrémités connues, poids finis ≥ 0).
  • S5 — Dépendances. matplotlib et folium passent dans l'extra [viz] (ils n'étaient importés que par le notebook). Classificateurs : Python 3.13 ; Windows / Linux / macOS à la place d'« OS Independent ».
  • compute_safe_routes relâche le GIL ; erreurs typées (ValueError, RuntimeError, BudgetExceededError) ; compute_routes lève RouteNotFoundError / SearchBudgetExceededError.
  • L'API REST répond 404 (aucun chemin), 422 (requête trop complexe), 503 (module natif absent) ; le calcul s'exécute hors de la boucle d'événements.
  • Le README et la documentation décrivent ce qui est vrai : l'algorithme est de type NAMOA* inspiré d'A*pex (pas de paires apex) ; la promesse « < 2 s sur Londres » est remplacée par les mesures.

Ajouté

  • bandwidth_m="auto" : bande passante choisie par log-vraisemblance de crimes mis de côté (temporelle si les crimes portent un mois, sinon aléatoire — optimiste). Opt-in : le défaut (Silverman borné à 800 m) est inchangé ; il sur-lisse sur Londres (voir docs/benchmarks.md).
  • SafeRouteEngine.load_graph(graphe, crimes) : n'importe quelle ville / vos propres données (S8).
  • S7 — Mesures reproductibles : benchmarks/run.py (chargement, KDE, construction Rust, requêtes par classe de distance, machine documentée) ; résultats dans benchmarks/results/.
  • S7 — Garantie ε vérifiée : tests Rust contre l'énumération exhaustive des chemins (250 graphes aléatoires) et l'algorithme de Martins (40 grands graphes), pour ε ∈ {0 ; 0,05 ; 0,1 ; 0,3 ; 0,5}.
  • Cache : un manque de RAM supprimait le graphe. GraphCache.load_graph effaçait le fichier sur TOUTE exception (une saturation mémoire a détruit un graphe de Londres de 527 Mo pendant les mesures). Les erreurs mémoire / E/S remontent sans toucher au fichier ; une vraie corruption met le fichier de côté (.corrupt). save_crimes est atomique (fichier temporaire puis renommage).
  • bandwidth_m explicite respecté tel quel (100 m devenait 200 m) ; seule la règle de Silverman est bornée.
  • S8 — saferoute.evaluation : évaluation du score de risque (AUC, lift, Spearman) avec protocole temporel et hold-out aléatoire (étiqueté optimiste) ; fetch_london_crimes conserve le mois.
  • docs/donnees.md : sources, licences, fraîcheur ; avertissement d'usage (README, API, doc).
  • 62 → 64+ tests Rust sans Python ; 106 → 168 tests Python (dont ceux du module natif, jamais exécutés avant).

Connu / non traité

  • La familiarité n'influence pas le choix des routes (voir README, « Limites connues »).
  • safest minimise le risque seul (peut être 3× plus long que shortest).
  • Trajets longs (4–10 km) : cas défavorables jusqu'au budget de labels (voir docs/benchmarks.md).
  • L'évaluation temporelle sur données réelles exige des crimes datés (le cache actuel n'en a pas).

0.1.0 — 2026-04-12

Ajouté

  • Algorithme A*pex biobjectif (distance + risque) avec ε-approximation configurable
  • Scoring KDE gaussien avec calibration automatique du bandwidth (règle de Silverman)
  • Variable de familiarité : C = w₁·D + w₂·R − w₃·F
  • 3 itinéraires Pareto-optimaux : shortest, safest, balanced
  • Métadonnées enrichies : estimated_time_min, comfort_score, node_count
  • Cache local .graphml + .csv.gz avec platformdirs (zéro chemin absolu)
  • API REST FastAPI : 7 endpoints (/route, /load-city, /risk-map, /nearest-node, /familiarity/*, /health)
  • Données réelles : Londres (501k nœuds, 100k crimes Police UK) + Le Cap (127k nœuds, SAPS)
  • Core Rust compilé via PyO3/maturin — saferoute_core.so
  • 102 tests Python + 20 tests Rust d'intégration
  • CI/CD GitHub Actions : 3 OS × 3 Python + publication TestPyPI/PyPI
  • Hiérarchie d'exceptions : SafeRouteError, GraphNotLoadedError, RouteNotFoundError, CacheCorruptionError, UnsupportedCityError
  • Documentation mkdocs avec mkdocstrings (docstrings Google)
  • Marqueur PEP 561 py.typed