MGS-10 a montré qu’un optimum placé à l’origine flatte les optimiseurs dont les opérateurs ou l’initialisation sont centrés en zéro (biais central). Il existe un second biais, complémentaire et tout aussi répandu : le biais d’alignement d’axes. Beaucoup d’opérateurs génétiques (crossover uniforme, mutation composante par composante, encerclement WOA) traitent chaque coordonnée indépendamment — ils sont « alignés sur les axes ». Lorsque le paysage présente une structure dans une direction non alignée avec les axes (la vallée en banane de Rosenbrock, qui courbe le long d’une parabole), ces opérateurs la traversent au lieu de la suivre.
La technique standard pour mesurer ce biais (compétitions CEC) : rotationner le paysage par une matrice orthogonale \(M\) (\(M M^{\top} = I\)). La rotation préserve les distances et la valeur de l’optimum, mais déplace le bassin dans une direction non alignée avec les axes. Ce notebook met en œuvre ce banc via le décorateur RotatedFitness du fork (analogue de rotation de ShiftedFitness), et pose deux questions :
Sanity — une rotation change-t-elle un paysage radial comme Sphere ? Non, par construction : c’est le contrôle que le décorateur est correct. Et les fonctions à terme périodique par axe (Rastrigin, Ackley) ? Si — la rotation brise leur séparabilité, et c’est déjà, en soi, le premier signal du biais.
Discrimination — sur un paysage asymétrique (Rosenbrock), la rotation déplace l’optimum : quels optimiseurs le retrouvent ?
Le décorateur de rotation : \(f_{\text{rot}}(x) = f(M x)\)
RotatedFitness enveloppe n’importe quelle fonction de fitness et l’évalue sur les coordonnées rotationnées\(y = M x\). La matrice \(M\) est orthogonale (\(M M^{\top} = I\)) : la transformation préserve les distances (norme), donc la valeur de l’optimum est inchangée, mais sa position est déplacée. La factory RotationMatrices.Seeded(n, graine) construit une telle matrice reproductible comme produit de rotations de Givens (une par paire d’indices) — produit de matrices orthogonales = orthogonal, garanti par construction.
Comme ShiftedFitness, c’est un décorateur compositionnel fin : les mathématiques de la fonction interne sont réutilisées telles quelles (jamais réimplémentées), seules les coordonnées qu’on lui fournit sont transformées. Les deux se composent pour la variante CEC complète « décalée puis rotationnée » : new RotatedFitness(new ShiftedFitness(inner, décalage), M).
// DoubleArrayChromosome : chromosome minimal stockant des gènes double nus.// (Réplique de l'assistant de test du fork ; bornes par gène pour que CreateNew()// randomise la population initiale — voir MGS-6 / MGS-10 pour le détail.)publicclass DoubleArrayChromosome : ChromosomeBase{privatereadonlydouble _min;privatereadonlydouble _max;publicDoubleArrayChromosome(double[] values,double min,double max):base(values.Length){ _min = min; _max = max;for(int i =0; i < values.Length; i++)ReplaceGene(i,newGene(values[i]));}publicoverride IChromosome CreateNew(){var rand = RandomizationProvider.Current;int n = Length;var vals =newdouble[n];for(int i =0; i < n; i++) vals[i]= rand.GetDouble(_min, _max);returnnewDoubleArrayChromosome(vals, _min, _max);}publicoverride Gene GenerateGene(int geneIndex)=>newGene(RandomizationProvider.Current.GetDouble(_min, _max));publicdouble[]GetDoubleValues()=>GetGenes().Select(g =>(double)g.Value).ToArray();}var probe =newDoubleArrayChromosome(newdouble[]{1.0,-2.0},-2.048,2.048);Console.WriteLine("DoubleArrayChromosome defini. Probe : "+ probe.GetDoubleValues().Length+" genes.");
DoubleArrayChromosome defini. Probe : 2 genes.
Réduire chaque optimiseur à un délégué Optimizer
Comme dans MGS-10, on emballe chaque construction de métaheuristique dans le contrat Optimizer du banc (taille de population 50, budget d’évaluations partagé). On garde un sous-ensemble représentatif qui couvre toute la gamme de structure d’opérateur :
GA à crossover uniforme — opérateur composante par composante (il échange des gènes), typiquement aligné sur les axes ;
BBPSO — échantillonnage gaussien par dimension (une loi normale par coordonnée, covariance diagonale) : son apparence « continue » ne le rend pas isotrope, un tirage par axe reste de la structure alignée sur les axes ;
WOA — encerclement par différence de positions, mais le coefficient d’encerclement est appliqué composante par composante (structure mixte) ;
DE — mutation par différence vectorielle\(F(a-b)\) : la seule dont le déplacement soit véritablement équivariant par rotation.
La recherche aléatoire sert de contrôle non biaisé. La partie D tranchera, à dimension assez élevée, lesquels résistent vraiment.
constint PopSize =50;// taille de population (GA + composés)constint NFE =2000;// budget d'évaluations (intentionnellement modeste : on veut// voir un écart de convergence, pas résoudre Rosenbrock à 0)IMetaHeuristic BuildGA(int g)=>newDefaultMetaHeuristic();IMetaHeuristic BuildWOA(int g)=> MetaHeuristicsService.CreateMetaHeuristicByName("WhaleOptimisation", maxGenerations: g, populationSize: PopSize);IMetaHeuristic BuildDE(int g)=> MetaHeuristicsService.CreateMetaHeuristicByName("DifferentialEvolution", maxGenerations: g, populationSize: PopSize);IMetaHeuristic BuildBBPSO(int g)=> MetaHeuristicsService.CreateMetaHeuristicByName("BareBonesParticleSwarm", maxGenerations: g, populationSize: PopSize);// Emballe n'importe quelle construction d'IMetaHeuristic dans le contrat Optimizer du banc.Optimizer MakeOptimizer(Func<int, IMetaHeuristic> buildMh){return(Optimizer)((req)=>{int gens = Math.Max(1, req.Evaluations/ PopSize);var(min, max)= req.Bounds;double mid =0.5*(min + max);var adam =newDoubleArrayChromosome(Enumerable.Repeat(mid, req.Dimension).ToArray(), min, max);var pop =newMetaPopulation(PopSize, PopSize, adam);var ga =newMetaGeneticAlgorithm( pop, req.Fitness,newEliteSelection(),newUniformCrossover(0.5f),newUniformMutation(true),buildMh(gens)); ga.Termination=newGenerationNumberTermination(gens); ga.Start();return ga.BestChromosome.Fitness??double.NegativeInfinity;});}var rs =newRandomSearchOptimizer(seed:7);Optimizer RandomOptimizer =(Optimizer)((req)=> rs.Run(req));// Ancrage RNG : reproductibilité d'une exécution à l'autre (valeur pédago : un étudiant// retrouve les valeurs committées). ResetSeed(42) avant le smoke test.FastRandomRandomization.ResetSeed(42);double smokeObj =-MakeOptimizer(BuildGA)(newOptimizerRequest(newSphereFitness(), KnownFunctionsBounds.For(typeof(SphereFitness)),5, NFE));Console.WriteLine("Smoke GA/Sphere : objectif = "+ smokeObj.ToString("G4")+" (proche de 0 = OK). Delegues prets : GA, WOA, DE, BBPSO, Random.");
Smoke GA/Sphere : objectif = 0,1384 (proche de 0 = OK). Delegues prets : GA, WOA, DE, BBPSO, Random.
Partie A — Rotation et symétrie : qui résiste, qui se trahit ?
Une fonction purement radiale (dont la valeur ne dépend que de la norme \(\|x\|\)) est invariante par rotation : une matrice orthogonale préserve la norme (\(\|M x\| = \|x\|\)), donc \(f(M x) = f(x)\) point par point. Sphere (\(\sum x_i^2\)) est de ce type — c’est notre contrôle de cohérence : si RotatedFitness changeait Sphere, le décorateur serait buggé.
Mais beaucoup de fonctions « standard » mélangent un terme radial et un terme périodique par axe (les \(\cos(2\pi x_i)\) de Rastrigin et Ackley). Ce terme est séparable — il traite chaque coordonnée indépendamment, donc il est aligné sur les axes. Une rotation mélange les coordonnées et brise cette séparabilité : la fonction n’est alors plus invariante. L’écart qu’on mesure ci-dessous quantifie précisément cela — et c’est déjà, en soi, le biais que la rotation cherche à révéler. On évalue chaque fonction en un point fixe, directement puis à travers RotatedFitness muni d’une matrice rotationnée reproductible.
// Partie A : rotation-invariance sur les fonctions symétriques (évaluation ponctuelle).// Dimension 2 (la rotation y est un seul angle, le cas le plus lisible).int dimA =2;int rotSeed =7;double[,] M = RotationMatrices.Seeded(dimA, rotSeed);Console.WriteLine("Matrice de rotation M ("+ dimA +"D, graine "+ rotSeed +") :");Console.WriteLine(" [ "+ M[0,0].ToString("F4")+" "+ M[0,1].ToString("F4")+" ]");Console.WriteLine(" [ "+ M[1,0].ToString("F4")+" "+ M[1,1].ToString("F4")+" ]");Console.WriteLine(" Orthogonale (M·Mᵀ = I) ? "+ RotationMatrices.IsOrthogonal(M));// Point de test (dans les bornes Rosenbrock ; seul le point importe, pas les bornes).double[] x ={1.5,-0.7};var chrom =newDoubleArrayChromosome(x,-2.048,2.048);IFitness sphere =newSphereFitness();IFitness rastrigin =newRastriginFitness();IFitness ackley =newAckleyFitness();// La fitness est NÉGATIVÉE (GeneticSharp maximise) ; on affiche l'objectif = -fitness.doubleObj(IFitness f)=>-f.Evaluate(newDoubleArrayChromosome(x,-2.048,2.048));Console.WriteLine();Console.WriteLine("Fonction objectif(x) objectif_rot(x) |écart|");Console.WriteLine(newstring('-',58));foreach(var(name, fn)innew(string, IFitness)[]{("Sphere", sphere),("Rastrigin", rastrigin),("Ackley", ackley)}){double direct =Obj(fn);double rotated =Obj(newRotatedFitness(fn, M)); Console.WriteLine(name.PadRight(14)+ direct.ToString("F6").PadLeft(12)+ rotated.ToString("F6").PadLeft(17)+ Math.Abs(direct - rotated).ToString("G2").PadLeft(10));}Console.WriteLine();Console.WriteLine("=> Sphere (radiale) : écart 0, le décorateur est validé. Rastrigin/Ackley : écart non nul,");Console.WriteLine(" leurs termes cosinus par axe brisent la séparabilité sous rotation (premier signal du biais).");
Matrice de rotation M (2D, graine 7) :
[ -0,7427 -0,6697 ]
[ 0,6697 -0,7427 ]
Orthogonale (M·Mᵀ = I) ? True
Fonction objectif(x) objectif_rot(x) |écart|
----------------------------------------------------------
Sphere 2,740000 2,740000 0
Rastrigin 35,830170 38,740040 2,9
Ackley 6,372836 6,443205 0,07
=> Sphere (radiale) : écart 0, le décorateur est validé. Rastrigin/Ackley : écart non nul,
leurs termes cosinus par axe brisent la séparabilité sous rotation (premier signal du biais).
Lecture. Trois régimes distincts apparaissent dans le tableau :
Sphere : écart nul à la précision machine. C’est la seule fonction purement radiale — le décorateur est validé (une fonction invariante par construction reste invariante).
Rastrigin : écart marqué. Son terme \(\sum_i \cos(2\pi x_i)\) est séparable (un cosinus par axe) ; la rotation mélange les axes et brise cette séparabilité. Rastrigin n’est pas invariante par rotation.
Ackley : écart faible mais non nul. Dominée par son terme radial \(\exp(-b\|x\|)\) (invariant), elle n’est perturbée que par son petit terme cosinus — d’où un effet réduit.
Voilà déjà une leçon : la rotation n’est pas qu’un artifice de test, elle mesure la séparabilité d’une fonction. Les fonctions séparables (un terme par axe) sont précisément celles qu’un optimiseur aligné sur les axes peut résoudre coordonnée par coordonnée ; les rotationner détruit cet avantage. C’est exactement pourquoi les suites CEC rotationnent systématiquement leurs fonctions.
Partie B — Discrimination : la rotation déplace l’optimum de Rosenbrock
La vallée en banane de Rosenbrock, \(f(x,y) = 100(y - x^2)^2 + (1 - x)^2\), est asymétrique : elle courbe le long d’une parabole, une direction bien précise (non alignée avec les axes après rotation). Son optimum global est en \((1, 1)\) (valeur \(0\)). Appliquons une rotation de 45° : \(M_{45} = \frac{\sqrt 2}{2}\begin{pmatrix}1 & -1\\ 1 & 1\end{pmatrix}\).
L’optimum ne disparaît pas (valeur conservée) mais se déplace : \(f_{\text{rot}}(x) = f(M_{45} x)\) est minimale là où \(M_{45} x = (1,1)\), soit \(x = M_{45}^{\top}(1,1) = (\sqrt 2, 0)\).
L’ancien optimum \((1,1)\) n’est plus optimal sous rotation : \(f_{\text{rot}}(1,1) = f(M_{45}(1,1)) = f(0, \sqrt 2) \approx 201\).
// Partie B : relocalisation de l'optimum de Rosenbrock sous rotation de 45°.double s = Math.Sqrt(2.0)/2.0;double[,] R45 ={{ s,-s },{ s, s }};// rotation de 45°Console.WriteLine("R45 orthogonale ? "+ RotationMatrices.IsOrthogonal(R45));IFitness rosen =newRosenbrockFitness();IFitness rosenRot =newRotatedFitness(rosen, R45);doubleObjAt(IFitness f,double a,double b)=>-f.Evaluate(newDoubleArrayChromosome(new[]{ a, b },-2.048,2.048));double oldCenter_val =ObjAt(rosen,1.0,1.0);// optimum non rotationnédouble oldCenter_rot =ObjAt(rosenRot,1.0,1.0);// (1,1) après rotationdouble newCenter_rot =ObjAt(rosenRot, Math.Sqrt(2.0),0.0);// (√2,0) = nouveau lieu de l'optimumConsole.WriteLine();Console.WriteLine("Rosenbrock en (1,1) [ancien optimum] : "+ oldCenter_val.ToString("F4")+" (= 0, optimum global)");Console.WriteLine("Rosenbrock roté en (1,1) [l'ancien n'est plus] : "+ oldCenter_rot.ToString("F4")+" (≈ 201, l'optimum a fui)");Console.WriteLine("Rosenbrock roté en (√2, 0) [nouvel optimum] : "+ newCenter_rot.ToString("F4")+" (≈ 0, l'optimum a déménagé ici)");Console.WriteLine();Console.WriteLine("=> la rotation n'a pas détruit l'optimum (valeur conservée ≈ 0), elle l'a DÉPLACÉ de (1,1) à (√2, 0).");Console.WriteLine(" Un optimiseur aligné sur les axes qui 'savait' descendre vers (1,1) cherche maintenant au mauvais endroit.");
R45 orthogonale ? True
Rosenbrock en (1,1) [ancien optimum] : 0,0000 (= 0, optimum global)
Rosenbrock roté en (1,1) [l'ancien n'est plus] : 201,0000 (≈ 201, l'optimum a fui)
Rosenbrock roté en (√2, 0) [nouvel optimum] : 0,0000 (≈ 0, l'optimum a déménagé ici)
=> la rotation n'a pas détruit l'optimum (valeur conservée ≈ 0), elle l'a DÉPLACÉ de (1,1) à (√2, 0).
Un optimiseur aligné sur les axes qui 'savait' descendre vers (1,1) cherche maintenant au mauvais endroit.
Lecture. La valeur de l’optimum est conservée (\(\approx 0\)) mais sa position a changé. C’est précisément le biais que la rotation expose : un optimiseur dont la dynamique est tacitement alignée avec les axes (init près d’une diagonale d’axes, déplacements composante par composante) « savait » où aller sur le paysage original ; sur le paysage rotationné, cette connaissance tacite est fausse. Reste à mesurer si nos optimiseurs le resentent.
Partie C — Les optimiseurs resentent-ils la rotation ?
On lance chaque optimiseur sur Rosenbrock non rotationné puis rotationné (même matrice M qu’en partie A, graine 7), en dimension 2, avec le même budget NFE. Pour isoler l’effet de la rotation (et pas celui du hasard), on reseme le RNG à l’identique avant chaque paire : les tirages initiaux sont donc les mêmes, seules les valeurs de fitness diffèrent — la différence de résultat est imputable au paysage.
La signature du biais est le delta : \(\Delta = \text{objectif}_{\text{roté}} - \text{objectif}_{\text{non roté}}\). - \(\Delta \approx 0\) : l’optimiseur est robuste à la rotation (ses déplacements ne privilégient pas les axes). - \(\Delta > 0\) : la rotation le dégrade (signature d’un alignement sur les axes).
// Partie C : signature delta (roté - non roté) par optimiseur sur Rosenbrock 2D.var bounds = KnownFunctionsBounds.For(typeof(RosenbrockFitness));// (-2.048, 2.048)double[,] Mc = RotationMatrices.Seeded(dimA, rotSeed);// même rotation qu'en partie Aint dimC =2;int pairSeed =42;var optsC =new(string Name, Optimizer Opt)[]{("Random(contrôle)", RandomOptimizer),("GA(uniform)",MakeOptimizer(BuildGA)),("WOA",MakeOptimizer(BuildWOA)),("DE",MakeOptimizer(BuildDE)),("BBPSO",MakeOptimizer(BuildBBPSO)),};Console.WriteLine("Optimiseur obj_nonroté obj_roté Δ = roté - non roté");Console.WriteLine(newstring('-',60));var deltas =new List<(string Name,double D)>();foreach(var(name, opt)in optsC){// Même graine avant chaque moitié de la paire -> tirages identiques, paysage seul diffère. FastRandomRandomization.ResetSeed(pairSeed);double objUnrot =-opt(newOptimizerRequest(newRosenbrockFitness(), bounds, dimC, NFE)); FastRandomRandomization.ResetSeed(pairSeed);double objRot =-opt(newOptimizerRequest(newRotatedFitness(newRosenbrockFitness(), Mc), bounds, dimC, NFE));double d = objRot - objUnrot; deltas.Add((name, d)); Console.WriteLine(name.PadRight(18)+ objUnrot.ToString("F4").PadLeft(12)+ objRot.ToString("F4").PadLeft(12)+ d.ToString("F4").PadLeft(13));}Console.WriteLine();Console.WriteLine("Δ > 0 = la rotation dégrade l'optimiseur (alignement sur les axes).");
Optimiseur obj_nonroté obj_roté Δ = roté - non roté
------------------------------------------------------------
Random(contrôle) 0,0557 0,0154 -0,0403
GA(uniform) 0,0776 0,0341 -0,0435
WOA 0,0000 0,0010 0,0010
DE 0,0000 0,0000 -0,0000
BBPSO 0,0179 0,0013 -0,0165
Δ > 0 = la rotation dégrade l'optimiseur (alignement sur les axes).
Lecture honnête : que dit le delta ?
Les données committées ci-dessus montrent un \(\Delta\)quasi nul pour tous les optimiseurs — aucun n’est dégradé par la rotation. Ce n’est pas un échec du banc, c’est le résultat attendu à ce budget et en dimension 2 : Rosenbrock 2D est assez facile pour que tous ces optimiseurs généraux (GA, WOA, DE, BBPSO) la résolvent essentiellement à zéro (objectifs de \(0{,}00\) à \(\sim 0{,}08\)), rotationnée ou non. Il n’y a alors pas de marge pour qu’un alignement sur les axes pénalise — quand tout le monde trouve l’optimum, la rotation ne peut pas discriminer.
La lecture méthodologique est importante : un banc de benchmark ne « prouve » un biais opérateur que si le problème est assez dur pour que le biais ait de la place pour se manifester. Ici le signal net est dans la géométrie du paysage (parties A et B) : la rotation brise la séparabilité des fonctions (Rastrigin) et déplace l’optimum des fonctions asymétriques (Rosenbrock). Pour voir l’effet opérateur — un \(\Delta\) qui sépare les déplacements composante par composant (crossover uniforme, WOA) des déplacements vectoriels (DE) — il faut complexisser le problème : dimension plus élevée et budget plus serré. C’est précisément l’objet de l’exercice 2.
On retrouve l’esprit de MGS-10 : ce n’est pas l’étiquette de la métaphore qui prédit le biais, c’est la structure de l’opérateur — et un banc honnête doit poser un problème assez dur pour que cette structure soit mise à l’épreuve.
→ La partie D ci-après complexifie le problème (dimension 5) et révèle effectivement cette signature opérateur.
Partie D — La signature révélée : plus haute dimension
La partie C, en dimension 2, n’a montré aucun écart : tous les optimiseurs résolvent Rosenbrock 2D (rotationnée ou non), il n’y a pas de marge pour qu’un alignement sur les axes pénalise. C’est la leçon méthodologique rappelée plus haut : un banc ne prouve un biais opérateur que si le problème est assez dur.
Montons en dimension 5, avec des bornes élargies \([-30, 30]\) (l’optimum rotationné a pour norme \(\sqrt d\), il faut de la place) et le même budget modeste. À cette dimension Rosenbrock n’est plus résolue à zéro — il reste de la marge. On rejoue le protocole (même RNG resemée avant chaque moitié de la paire) sur 5 graines et l’on prend la médiane, pour réduire le bruit. C’est le régime CEC canonique où la rotation discrimine.
Lecture. Cette fois le signal apparaît nettement. À la dimension 5 il reste de la marge (les objectifs sont loin de zéro), et la rotation ne touche pas tous les opérateurs de la même façon :
DE (mutation par différence vectorielle\(F(a-b)\)) est le seul clairement robuste (\(\Delta \approx 2\)) : son déplacement est une combinaison linéaire de positions, équivariante par rotation — la direction du pas suit le paysage, pas les axes.
GA à crossover uniforme (\(\Delta \approx 2847\)), WOA (\(\Delta \approx 1289\)) et BBPSO (\(\Delta \approx 981\)) sont tous dégradés par la rotation :
le crossover uniforme échange des composantes (aligné sur les axes) ;
BBPSOéchantillonne une gaussienne par dimension (covariance diagonale) — un tirage par axe, c’est bien de la structure alignée sur les axes ;
WOA, malgré son encerclement par différence de positions, applique son coefficient d’encerclement composante par composante, ce qui laisse assez de structure alignée sur les axes pour être pénalisé.
La recherche aléatoire a un \(\Delta\) négatif non interprétable : ce n’est pas un optimiseur, sa « performance » n’est que du bruit d’échantillonnage.
La leçon se précise : pour résister à la rotation, il ne suffit pas d’être « stochastique » ou « continu », il faut un opérateur véritablement vectoriel — un déplacement qui soit une combinaison linéaire de points du paysage (la différence différentielle de DE). Tout ce qui traite les coordonnées axe par axe (crossover uniforme, gaussienne par dimension, coefficient scalaire composante par composante) porte un alignement que la rotation révèle dès que le problème est assez dur. C’est la thèse « components over metaphors » de Sorensen (2015), dans sa version la plus fine : c’est la structure géométrique du déplacement, pas le nom de l’algorithme, qui prédit le biais.
Conclusion : décalage et rotation, les deux biais du banc CEC
Ce notebook complète MGS-10 : ensemble, ils instrumentent les deux techniques de dé-biaisement des bancs modernes (CEC) —
Biais
Ce qu’il flatte
Décorateur fork
Notebook
Central
optimum à l’origine, init/centre en zéro
ShiftedFitness
MGS-10
Alignement d’axes
opérateurs composante par composant
RotatedFitness
MGS-12 (celui-ci)
Les deux sont des décorateurs compositionnels : ils réutilisent les mathématiques des fonctions canoniques sans les réimplémenter, et se composent (new RotatedFitness(new ShiftedFitness(inner, o), M)) pour la variante CEC complète. La leçon tient : on juge un optimiseur sur la structure de ses opérateurs (vectoriels vs composante par composant, centrés vs non), pas sur le nom de sa métaphore — et un banc honnête doit tester les deux biais.
La partie D boucle le propos : à dimension assez élevée pour qu’il reste de la marge, la rotation discrimine les optimiseurs selon la structure de leurs opérateurs (composante par composant vs vectoriel) — la signature que les métaphores ne prédisent pas mais que la géométrie de l’opérateur, elle, révèle.
Exercices
Convention : les exercices sont des cellules à compléter. Squelette fourni, aucune erreur levée — le notebook s’exécute de bout en bout même non terminé.
Exercice 1 : la signature opérateur (vectoriel vs composante par composant)
Ajoutez DE/best/1 ou un crossover arithmétique entier (moyenne pondérée des deux parents, \(\alpha A + (1-\alpha)B\)) au banc de la partie C. Le crossover arithmétique est-il plus ou moins robuste à la rotation que le crossover uniforme ? Mesurez le \(\Delta\) et confrontez à votre intuition sur la structure de l’opérateur.
Indice : le crossover arithmétique produit un descendant sur le segment entre les parents (combinaison convexe) — une opération géométrique indépendante du repère, donc a priori équivariante par rotation.
// Exercice 1 : ajouter un optimiseur à crossover arithmétique au banc de la partie C.// TODO étudiant : construire un Optimizer utilisant WholeArithmeticCrossover (ou équivalent),// le lancer sur Rosenbrock non roté puis roté (même protocole ResetSeed(pairSeed) par moitié),// et afficher son Δ à côté des autres.// result = null // TODO étudiantConsole.WriteLine("Exercice 1 à compléter : Δ du crossover arithmétique vs uniforme.");
Exercice 1 à compléter : Δ du crossover arithmétique vs uniforme.
Exercice 2 : la dimension amplifie-t-elle encore le biais ?
La partie D a révélé la signature à la dimension 5. Reprenez son protocole à la dimension 10 (bornes \([-30, 30]\), mêmes 5 graines, médiane). Le \(\Delta\) des opérateurs alignés sur les axes (GA uniforme, BBPSO) grandit-il avec la dimension ? C’est l’intuition CEC : plus la dimension est élevée, plus la rotation est discriminante. Attention au budget : à très haute dimension tout le monde échoue et le signal disparaît — gardez un NFE où il reste de la marge.
Étape 1 : remplacer dimD par 10. Étape 2 : relancer la paire roté/non roté sur les 5 graines. Étape 3 : comparer les Δ à ceux de la dimension 5.
// Exercice 2 : signature delta de Rosenbrock roté en dimension 10 (vs dimension 5 de la partie D).// TODO étudiant : adapter la boucle de la partie D avec dimD = 10.Console.WriteLine("Exercice 2 à compléter : Δ en dimension 10 vs dimension 5.");
Exercice 2 à compléter : Δ en dimension 10 vs dimension 5.
Exercice 3 : la variante CEC complète (décalée PUIS rotationnée)
Composez les deux décorateurs : new RotatedFitness(new ShiftedFitness(new RosenbrockFitness(), offset), M) avec un offset non nul (via ShiftVectors.Seeded) et la matrice M. Vérifiez que l’optimum est à la fois décalé et rotationné, et lancez un optimiseur : combine-t-on les deux difficultés ? Quelle borne faut-il pour rester dans le domaine ?
Indice : l’optimum est en \(M^{\top}(1,1) + o\) ; choisir bornes et offset pour qu’il reste atteignable.
// Exercice 3 : variante CEC décalée + rotationnée.// TODO étudiant : construire new RotatedFitness(new ShiftedFitness(rosen, offset), M),// localiser le nouvel optimum (Mᵀ·(1,1) + offset), vérifier sa valeur ≈ 0, lancer un optimiseur.Console.WriteLine("Exercice 3 à compléter : banc CEC shift+rotate complet.");
Exercice 3 à compléter : banc CEC shift+rotate complet.