Tests : comparaison du moteur embarqué aux références de gnubg
tests/test_engine_reference.sh lance tests/reference/compare.py sur les 32 positions aux niveaux 0 et 2 plis. Tolérance de 0,002 mesurée et justifiée dans tests/reference/README.md : écart maximal de 9,5e-4, avec ou sans -ffast-math ; la cause est la sigmoïde approchée (_mm_rcp_ps) de gnubg de bureau en SSE2, qu'un build SSE2 du moteur reproduit au bit près. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ae166d51e5
commit
83cb1a6f83
@@ -12,13 +12,15 @@ d'un fichier de résultat.
|
||||
| `positions.json` | Positions d'entrée, versionnées, écrites à la main |
|
||||
| `results/<id>.json` | Résultats de gnubg, un fichier par position, générés |
|
||||
| `generate.py` | Script exécuté dans gnubg qui produit `results/` |
|
||||
| `compare.py` | Compare le moteur embarqué à `results/` (dernière section) |
|
||||
|
||||
Commandes :
|
||||
|
||||
```
|
||||
make reference-image # construit l'image Docker du gnubg de référence
|
||||
make reference # régénère results/ (une vingtaine de secondes)
|
||||
make test # vérifie la cohérence (tests/test_reference.sh)
|
||||
make test # vérifie la cohérence (tests/test_reference.sh) et
|
||||
# compare le moteur embarqué (tests/test_engine_reference.sh)
|
||||
```
|
||||
|
||||
`make reference` est déterministe : deux exécutions donnent des fichiers
|
||||
@@ -222,8 +224,86 @@ Sortie de `gnubg.cfevaluate` (`GeneralCubeDecisionE` puis
|
||||
|
||||
## Comparaison avec le moteur embarqué
|
||||
|
||||
Une concordance au bit près n'est pas attendue (ADR 0005) : `-ffast-math` et
|
||||
le jeu d'instructions (SSE2 ici, NEON sur téléphone, C générique en
|
||||
WebAssembly) changent les derniers chiffres. Les tests de concordance devront
|
||||
donc comparer les nombres avec une tolérance, et accepter qu'entre deux coups
|
||||
d'équités presque égales l'ordre s'inverse.
|
||||
`make test` compare le moteur embarqué à ces résultats
|
||||
(`tests/test_engine_reference.sh`). Pour chaque position et chaque niveau, le
|
||||
comparateur `compare.py` (Python 3, dans l'image Docker de la chaîne C) lance
|
||||
|
||||
```
|
||||
build/engine-cli --ply N reference POSITION_ID MATCH_ID [DÉS]
|
||||
```
|
||||
|
||||
qui produit un objet de même structure que `plies["<n>"]` : `evaluation`,
|
||||
puis `moves` (avec les dés) ou `cube` (sans). Le classement des coups suit la
|
||||
méthode de `hint` décrite plus haut (`engine_best_moves` dans
|
||||
`engine/engine.c`). Pour voir tous les nombres comparés :
|
||||
`BUILD_C_IMAGE=ustagammon/build-c:2 tests/test_engine_reference.sh --details`.
|
||||
|
||||
Une concordance au bit près n'est pas attendue (ADR 0005) : le jeu
|
||||
d'instructions (SSE2 pour gnubg de bureau, C générique pour le moteur
|
||||
embarqué sur PC et en WebAssembly, NEON sur téléphone) change les derniers
|
||||
chiffres. Les nombres sont donc comparés avec une tolérance.
|
||||
|
||||
### Critères
|
||||
|
||||
Pour chaque position et chaque niveau :
|
||||
|
||||
- même nombre de coups légaux ;
|
||||
- même meilleur coup, sauf si gnubg lui-même sépare ses deux premiers coups
|
||||
de moins que la tolérance : c'est alors une égalité, signalée par une
|
||||
remarque dans le rapport, sans échec ;
|
||||
- même pli réel du meilleur coup ;
|
||||
- pour chaque coup présent dans les deux classements (10 premiers), équité
|
||||
et cinq probabilités à la tolérance près ;
|
||||
- évaluation de la position (cinq probabilités et équité) à la tolérance
|
||||
près ;
|
||||
- même décision de videau (`decision` et `decision_text`), et les quatre
|
||||
équités du videau à la tolérance près.
|
||||
|
||||
Le rapport donne, pour chaque écart hors critères, la position et le niveau,
|
||||
le champ, la valeur attendue (gnubg), la valeur obtenue (moteur) et l'écart ;
|
||||
il donne toujours l'écart maximal observé.
|
||||
|
||||
### Tolérance : 0,002
|
||||
|
||||
Une même tolérance absolue s'applique aux probabilités et aux équités
|
||||
(équités ramenées à un videau de 1). Elle vient de mesures faites le
|
||||
2026-11-02 sur les 32 positions aux deux niveaux (2 598 nombres comparés) :
|
||||
|
||||
| Moteur comparé à gnubg de bureau | 99 % des écarts sous | Écart maximal |
|
||||
| --- | --- | --- |
|
||||
| Moteur embarqué tel que compilé (C générique, `-O3 -ffast-math`) | 1,6e-4 | 9,5e-4 |
|
||||
| Le même sans `-ffast-math` | 1,6e-4 | 9,5e-4 |
|
||||
| Le même compilé en SSE2 comme gnubg de bureau (essai, non retenu) | 0 | 0 (float32 identiques) |
|
||||
|
||||
Ce que ces mesures montrent :
|
||||
|
||||
- **`-ffast-math` n'est pas en cause côté moteur embarqué** : avec ou sans,
|
||||
le moteur en C générique donne les mêmes classements, et ses nombres
|
||||
diffèrent entre eux de 1,1e-6 au plus.
|
||||
- **La cause est le code SIMD de gnubg de bureau.** En SSE2 avec
|
||||
`-ffast-math`, la sigmoïde du réseau de neurones (`sigmoid_positive_ps`
|
||||
dans `lib/neuralnetsse.c`) calcule `1/x` avec l'instruction approchée
|
||||
`_mm_rcp_ps` (environ 12 bits de précision), alors que la version en C
|
||||
générique (`sigmoid` dans `lib/sigmoid.h`) fait une vraie division. L'essai
|
||||
le prouve : le même moteur compilé en SSE2 (`-DUSE_SIMD_INSTRUCTIONS
|
||||
-DHAVE_SSE -DUSE_SSE2`) redonne les références au bit près, sur les 2 604
|
||||
nombres comparés (classements identiques, donc un peu plus de coups
|
||||
communs).
|
||||
- **L'écart reste en général de l'ordre de 1e-4**, sauf un cas :
|
||||
`opening-64-match` à 2 plis, coup `8/2 6/2`, écart de 9,5e-4 alors que les
|
||||
autres coups de la même position restent sous 1e-5. Une recherche à 2 plis
|
||||
fait des choix discrets (meilleure réponse pour chaque jet, élagage) : quand
|
||||
deux choix sont presque à égalité, un écart de 1e-4 peut en faire basculer
|
||||
un, et l'écart final grandit d'autant.
|
||||
|
||||
La tolérance de 0,002 vaut environ deux fois l'écart maximal mesuré, pour
|
||||
absorber d'autres bascules de ce genre sans masquer un vrai défaut : elle
|
||||
reste quinze fois plus petite que le seuil à partir duquel gnubg qualifie un
|
||||
coup de « douteux » (0,03) et correspond à deux unités du dernier chiffre
|
||||
qu'affiche gnubg (trois décimales). Les vrais défauts (mauvais niveau,
|
||||
mauvais contexte de videau, mauvaise conversion d'équité) donnent des écarts
|
||||
d'un ou plusieurs centièmes.
|
||||
|
||||
Sur téléphone (NEON) les écarts seront à mesurer de la même façon : avec
|
||||
`-ffast-math`, gnubg y remplace aussi la division par une approximation
|
||||
(`vrecpeq_f32` et une itération de Newton), différente de celle de SSE2.
|
||||
|
||||
Reference in New Issue
Block a user