MGS-6 comparait les composés (WOA, EO, FBI) au GA « de base » sur dix fonctions, à nombre de générations égal, et montrait qu’aucune métaheuristique « publiée » ne bat systématiquement la composition. Mais un banc qui compte les générations laisse une porte ouverte : si un algorithme concentre ses échantillons près du centre du domaine — là où, par construction, beaucoup de fonctions test placent leur optimum — il peut imiter la performance sans rien comprendre au paysage. C’est la critique de Kudela (Nature Machine Intelligence, 2022) : la plupart des métaheuristiques récentes sont biaisées vers le centre, et ce biais se révèle dès qu’on déplace l’optimum hors du centre.
Ce notebook rend cette critique concrète et mesurable, en câblant le banc d’essai CenterBiasBenchmark du fork (PR #14 + #15, voir #1203) : chaque optimiseur reçoit le même budget en évaluations (NFE, comme mealpy) et est testé sur la fonction centrée (optimum au centre) puis déplacée (optimum relocalisé par un vecteur aléatoire reproductible). La signature du biais est le delta : $= $ objectif(déplacé) \(-\) objectif(centré). En principe, un optimiseur non biaisé couvre le domaine uniformément et obtient \(\Delta \approx 0\) ; un optimiseur biaisé vers le centre fait nettement mieux au centre qu’ailleurs, donc \(\Delta \gg 0\). En pratique — et c’est ce que ce notebook montre — \(\Delta\) ne se lit pas seul : il faut le confronter à la performance absolue, car un optimiseur trop faible a un \(\Delta\) bruité (variance), pas un \(\Delta\) signé (biais).
Objectifs d’apprentissage
Comprendre pourquoi un banc à budget égal en générations peut masquer un biais central.
Câbler le protocole centré-vs-déplacé (Kudela 2022) via CenterBiasBenchmark.RunSuite et le délégué Optimizer.
Mesurer la signature de biais \(\Delta\) pour le GA, WOA, EO, FBI, et apprendre à la lire conjointement à la performance absolue (car un \(\Delta\) élevé peut signifier un biais… ou simplement un optimiseur trop faible).
Le banc CenterBiasBenchmark met chaque optimiseur à l’épreuve sur deux versions d’une même fonction :
Centrée — la fonction telle que publiée. Pour Sphere, Rastrigin, Ackley, l’optimum global est à l’origine, qui est aussi le centre du domaine symétrique \([-b, +b]^n\). Un algorithme qui échantillonne près du centre touche donc l’optimum « par construction ».
Déplacée — la même fonction, mais l’optimum est relocalisé hors du centre. ShiftedFitness soustrait un vecteur offset aux coordonnées avant l’évaluation : évaluer \(f(x - \text{offset})\) place l’optimum en \(x = \text{offset}\). Le vecteur est tiré une fois pour toutes par ShiftVectors.Seeded(n, magnitude, seed) — chaque coordonnée dans \([-\text{magnitude}, +\text{magnitude}]\), reproductible (même graine → même déplacement, d’une machine à l’autre).
Les deux runs partagent le même budget en évaluations (NFE), pas le même nombre de générations : c’est la règle d’équité de mealpy, portée par EvaluationBudget. La signature du biais est :
\(\Delta \approx 0\) : l’optimiseur résout la fonction où que soit l’optimum → non biaisé (la recherche aléatoire uniforme est le contrôle canonique, à condition d’un budget suffisant pour approcher l’optimum).
\(\Delta \gg 0\) : l’optimiseur résout le cas centré mais échoue une fois déplacé → il exploite le placement central de l’optimum → biaisé vers le centre.
Mise en garde de lecture.\(\Delta\) n’est un signal de biais que si l’optimiseur résolvait effectivement le cas centré. Un optimiseur trop faible — qui n’atteint l’optimum ni centré ni déplacé — a un \(\Delta\) dominé par la variance d’échantillonnage, sans rien révéler d’un biais central. D’où l’importance de lire \(\Delta\)conjointement à la performance absolue (la valeur centrée).
Note d’honnêteté. Le chromosome « adam » qui amorce la population GA est semé au centre du domaine (convention MGS, cf. MGS-6) ; les autres individus sont tirés uniformément par CreateNew(). Cette amorce donne au GA un léger avantage sur le cas centré (l’adam tombe à l’optimum exact pour ces fonctions dont l’optimum est 0 = centre) ; la part dominante de \(\Delta\) vient néanmoins de la dynamique des opérateurs (croisement-moyenne, élitisme) qui, sur un domaine symétrique, ramène la population vers le centre. Le contrôle « recherche aléatoire » n’a aucun adam.
// 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 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,3.14},-5.12,5.12);Console.WriteLine("DoubleArrayChromosome defini. Probe : "+ probe.GetDoubleValues().Length+" genes (borne dans [-5.12, 5.12]).");
DoubleArrayChromosome defini. Probe : 3 genes (borne dans [-5.12, 5.12]).
Réduire chaque optimiseur à un délégué Optimizer
Le banc est agnostique à l’optimiseur : il ne connaît que le contrat delegate double Optimizer(OptimizerRequest) — « consomme ce budget d’évaluations sur cette boîte, renvoie la meilleure fitness ». Nous emballons donc chaque métaheuristique dans une fonction MakeOptimizer qui reconstruit un MetaGeneticAlgorithm à chaque appel (run centré, puis run déplacé), en dérivant le nombre de générations du budget NFE : generations = floor(NFE / populationSize).
Les composés géométriques (WOA, EO, FBI) sont reconstruits par la factory du fork MetaHeuristicsService.CreateMetaHeuristicByName, qui câble automatiquement un convertisseur d’identité gene↔︎double (le câblage que la couche géométrique exige). Le contrôle non biaisé est RandomSearchOptimizer, qui échantillonne uniformément sans aucune dynamique de population.
constint PopSize =50;// taille de population (GA + composés)constint NFE =5000;// budget d'évaluations partagé (règle mealpy) -> generations = 100// Factory : reconstruit un composé nommé avec le bon MaxGenerations (factory prouvée MGS-5).IMetaHeuristic BuildGA(int g)=>newDefaultMetaHeuristic();IMetaHeuristic BuildWOA(int g)=> MetaHeuristicsService.CreateMetaHeuristicByName("WhaleOptimisation", maxGenerations: g, populationSize: PopSize);IMetaHeuristic BuildEO(int g)=> MetaHeuristicsService.CreateMetaHeuristicByName("EquilibriumOptimizer", maxGenerations: g, populationSize: PopSize);IMetaHeuristic BuildFBI(int g)=> MetaHeuristicsService.CreateMetaHeuristicByName("ForensicBasedInvestigation", 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);IMetaHeuristic BuildSA(int g)=> MetaHeuristicsService.CreateMetaHeuristicByName("SimulatedAnnealing", 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;});}// Contrôle non biaisé : recherche aléatoire uniforme (aucune population, aucune dynamique).// Même graine pour les runs centré et déplacé -> mêmes points tirés -> comparaison équitable.var rs =newRandomSearchOptimizer(seed:7);Optimizer RandomOptimizer =(Optimizer)((req)=> rs.Run(req));// Smoke test : chaque optimiseur sur Sphere centrée (l'optimum 0 doit être approché).// Ancre aussi le RNG du smoke test -> tout le notebook est reproductible d'un run a l'autre.FastRandomRandomization.ResetSeed(42);double centeredSphere =MakeOptimizer(BuildGA)(newOptimizerRequest(newSphereFitness(), KnownFunctionsBounds.For(typeof(SphereFitness)),5, NFE));Console.WriteLine("Smoke GA/Sphere centree : objectif = "+(-centeredSphere).ToString("G4")+" (proche de 0 = OK)");double rsSphere =RandomOptimizer(newOptimizerRequest(newSphereFitness(), KnownFunctionsBounds.For(typeof(SphereFitness)),5, NFE));Console.WriteLine("Smoke Random/Sphere centree : objectif = "+(-rsSphere).ToString("G4")+" (moins bon, normal pour NFE="+ NFE +")");Console.WriteLine("Delegues Optimizer prets : GA, WOA, EO, FBI, DE, BBPSO, SA, Random(control).");
Smoke GA/Sphere centree : objectif = 0,008247 (proche de 0 = OK)
Smoke Random/Sphere centree : objectif = 0,6408 (moins bon, normal pour NFE=5000)
Delegues Optimizer prets : GA, WOA, EO, FBI, DE, BBPSO, SA, Random(control).
L’expérience : la suite centrée-vs-déplacée
Nous lançons CenterBiasBenchmark.RunSuite pour les huit optimiseurs (Random, GA, WOA, EO, FBI, DE, BBPSO, recuit simulé) sur trois fonctions canoniques — Sphere (unimodale lisse), Rastrigin (fortement multimodale), Ackley (puits central entouré de rides) — en dimension 5, avec un budget NFE = 5000 partagé et un déplacement d’amplitude 2.0 (l’optimum se déplace d’au plus 2 unités par axe, reste largement dans les bornes). Chaque fonction reçoit un déplacement distinct (seed + index) afin que les relocalisations ne s’alignent pas sur la diagonale du domaine.
Reproductibilité. Les sept optimiseurs à population tirent leur stochasticité du RNG global du framework, qui par défaut n’est pas seedé. On l’ancre donc une fois avant la suite (FastRandomRandomization.ResetSeed(masterSeed)) : chaque optimiseur consomme alors un flux déterministe, et les chiffres committés sont reproductibles d’une exécution à l’autre — un étudiant qui ré-exécute le notebook retrouve exactement les valeurs de la table. Le contrôle RandomSearchOptimizer reste seedé séparément (seed: 7).
var budget =newEvaluationBudget(NFE);double shiftMag =2.0;int masterSeed =42;int dim =5;var problems =new List<(IFitness fitness,int dimension)>{(newSphereFitness(), dim),(newRastriginFitness(), dim),(newAckleyFitness(), dim),};var optimizers =new(string Name, Optimizer Opt)[]{("Random(control)", RandomOptimizer),("GA(uniform)",MakeOptimizer(BuildGA)),("WOA",MakeOptimizer(BuildWOA)),("EO",MakeOptimizer(BuildEO)),("FBI",MakeOptimizer(BuildFBI)),("DE",MakeOptimizer(BuildDE)),("BBPSO",MakeOptimizer(BuildBBPSO)),("SA",MakeOptimizer(BuildSA)),};// Reproductibilite : on reseme le RNG global du framework UNE FOIS avant la suite.// Par defaut RandomizationProvider.Current est un FastRandomRandomization non seede ->// les nombres re-roulaient a chaque execution. ResetSeed(masterSeed) ancre le flux une fois ;// chaque optimiseur continue le meme flux determine -> chiffres reproduisibles d'une// execution a l'autre (valeur pedago : un etudiant retrouve les valeurs committes).FastRandomRandomization.ResetSeed(masterSeed);var rows =new List<(string Opt, CenterBiasResult R)>();foreach(var(optName, opt)in optimizers){var results = CenterBiasBenchmark.RunSuite(problems, budget, opt, shiftMag, masterSeed);foreach(var r in results){ rows.Add((optName, r)); Console.WriteLine(optName.PadRight(16)+" "+ r.ToRow());}}Console.WriteLine();Console.WriteLine("--- Suite terminee : 8 optimiseurs x 3 fonctions, chacune en centre + deplace ---");
// Tableau de synthese : le delta (signature du biais) par fonction x optimiseur + moyenne par optimiseur.// (Chart de biais en fin de cellule : SVG inline zero-dep via display(HTML(...)), technique C548-L2.// Rend sur GitHub/nbviewer/offline - remplace le CDN-script Plotly (<script src=cdn.plot.ly>// sandboxe par GitHub -> rendu BLANC en consultation statique). Cf #6927 / #3801 Prong-A.)using Microsoft.DotNet.Interactive.Formatting;using System.IO;using System.Globalization;using System.Text;// Format invariant (decimal DOT) : critique pour le rendu SVG (comma = separateur de coordonnee// -> casse le parse Chromium sur machine fr-FR). Idiome canon SvgChartHelper.cs (#6942).stringF(double v)=> v.ToString("0.###", CultureInfo.InvariantCulture);var functions = rows.Select(r => r.R.Function).Distinct().ToList();Console.WriteLine("Delta (objectif deplace - objectif centre) : ~0 = non biaise, >0 = biaise vers le centre.");Console.WriteLine();string hdr ="Optimiseur "+string.Join("", functions.Select(f => f.PadLeft(16)))+" moyenne";Console.WriteLine(hdr);Console.WriteLine(newstring('-', hdr.Length));var byOpt = rows.GroupBy(r => r.Opt).ToList();double maxMean = byOpt.Max(g => g.Average(x => x.R.Delta));foreach(var g in byOpt){var byFn = g.ToDictionary(x => x.R.Function, x => x.R.Delta);double mean = g.Average(x => x.R.Delta);string row = g.Key.PadRight(16);foreach(var fn in functions) row +=(byFn.TryGetValue(fn,outvar d)? d.ToString("F3"):"-").PadLeft(16); row += mean.ToString("F3").PadLeft(10); Console.WriteLine(row);}// Diagramme en barres horizontales Plotly : signature de biais moyenne par optimiseur.// Contrairement a la barre ASCII (qui masquait les deltas negatifs via Math.Max(0, mean)),// le chart SIGNE montre les deux sens du biais : >0 = tire vers le centre (WOA, Random),// <0 = anti-centre (DE, BBPSO). La ligne zero (zeroline) separe les deux regimes.{// Barres horizontales SIGNEES en SVG inline : >0 = tire vers le centre (bleu),// <0 = anti-centre (orange). La zeroline (x=0) separe les deux regimes - contrairement// a la barre ASCII (Math.Max(0, mean)) qui masquait les deltas negatifs.var ordered = byOpt.OrderByDescending(g => g.Average(x => x.R.Delta)).ToList();var names = ordered.Select(g => g.Key).ToArray();var means = ordered.Select(g => Math.Round(g.Average(x => x.R.Delta),3)).ToArray();int n = ordered.Count;int W =720, marginL =110, marginR =70, marginT =54, marginB =48;int plotW = W - marginL - marginR;int rowH =34;int H = marginT + marginB + n * rowH;int plotH = H - marginT - marginB;double absMax = means.Max(mv => Math.Abs(mv));if(absMax <1e-9) absMax =1;double zeroX = marginL + plotW /2.0;doubleBarW(double v)=>(Math.Abs(v)/ absMax)*(plotW /2.0);doubleBarX(double v)=> v >=0? zeroX : zeroX -BarW(v);doubleRowY(int i)=> marginT + i * rowH + rowH /2.0;var sb =newStringBuilder(); sb.Append($"<svg viewBox='0 0 {W} {H}' xmlns='http://www.w3.org/2000/svg' "+ $"font-family='-apple-system,Segoe UI,sans-serif' font-size='12'>"); sb.Append($"<rect width='{W}' height='{H}' fill='white'/>"); sb.Append($"<text x='{W/2}' y='20' text-anchor='middle' font-size='14' font-weight='bold' fill='#333'>"+ $"Signature de biais moyenne par optimiseur</text>"); sb.Append($"<text x='{W/2}' y='38' text-anchor='middle' font-size='11' fill='#666'>"+ $"delta > 0 = centre, < 0 = anti-centre</text>");// Grille verticale (5 lignes) + labels axe xfor(int g =0; g <=4; g++){double v =-absMax +(2* absMax)* g /4.0;double gx = marginL + plotW * g /4.0;bool isZero = Math.Abs(v)< absMax *1e-6; sb.Append($"<line x1='{F(gx)}' y1='{marginT}' x2='{F(gx)}' y2='{marginT + plotH}' "+ $"stroke='{(isZero ? "#444" : "#e8e8e8")}' stroke-width='{(isZero ? 2 : 1)}'/>"); sb.Append($"<text x='{F(gx)}' y='{H - marginB + 16}' text-anchor='middle' fill='#666'>{F(Math.Round(v,2))}</text>");} sb.Append($"<text x='{W/2}' y='{H - 8}' text-anchor='middle' fill='#555'>Delta moyen (objectif deplace - objectif centre)</text>");// Barres + labelsfor(int i =0; i < n; i++){double ymid =RowY(i);double bw =BarW(means[i]);double bx =BarX(means[i]);string color = means[i]>=0?"#2a6dba":"#e8743b"; sb.Append($"<rect x='{F(bx)}' y='{F(ymid - 11)}' width='{F(bw)}' height='22' fill='{color}' rx='2'/>");bool rightSide = means[i]>=0;double lx = rightSide ? bx + bw +5: bx -5;string anchor = rightSide ?"start":"end"; sb.Append($"<text x='{F(lx)}' y='{F(ymid + 4)}' text-anchor='{anchor}' fill='#333' font-size='11'>{F(means[i])}</text>"); sb.Append($"<text x='{marginL - 8}' y='{F(ymid + 4)}' text-anchor='end' fill='#333'>{names[i]}</text>");}display(HTML($"<div>{sb}</div>"));}
Delta (objectif deplace - objectif centre) : ~0 = non biaise, >0 = biaise vers le centre.
Optimiseur SphereFitnessRastriginFitness AckleyFitness moyenne
---------------------------------------------------------------------------
Random(control) 0,205 7,936 0,117 2,753
GA(uniform) 0,005 -0,793 0,419 -0,123
WOA 0,002 10,592 4,358 4,984
EO 0,000 1,990 0,000 0,663
FBI 0,000 0,001 0,002 0,001
DE 0,000 -6,442 0,000 -2,147
BBPSO 0,000 -4,975 0,000 -1,658
SA -0,000 4,977 0,001 1,659
Lecture : que dit réellement le delta ?
Le tableau révèle une histoire plus nuancée que le scénario « propre » (Random ≈ 0, tous les autres ≫ 0) — et c’est précisément cette nuance qui fait la valeur du protocole. Trois régimes apparaissent, selon que l’optimiseur résout ou non la fonction à ce budget (NFE = 5000, dimension 5) :
L’optimiseur est robuste au déplacement → \(\Delta \approx 0\) : il atteint une qualité similaire centré ou déplacé. C’est le cas du GA (uniform) (Sphere \(+0{,}005\), Rastrigin \(-0{,}8\), Ackley \(+0{,}4\)) et surtout de DE (Sphere \(\approx 0\), Rastrigin \(-6{,}4\), Ackley \(\approx 0\)). Ces deux assemblages non métaphoriques n’ont aucune préférence centrale — DE trouve même un meilleur optimum déplacé sur Rastrigin (\(\Delta < 0\)). EO et FBI résolvent eux aussi Sphere et Ackley (\(\Delta \approx 0\) sur ces deux).
L’optimiseur résout le cas centré mais échoue déplacé → \(\Delta \gg 0\) : c’est la signature nette du biais central. Le cas le plus franc est WOA sur Ackley : centré, WOA atteint \(0{,}0\) (l’optimum central) ; déplacé, il plonge à \(4{,}4\). Son bubble-net converge vers la région centrale et rate l’optimum relocalisé. EO montre le même phénomène sur Rastrigin : il y trouve l’optimum centré (\(0{,}0\)) mais échoue déplacé (\(2{,}0\)). FBI, lui, résout Rastrigin centré (\(0{,}0\)) ET déplacé (\(\approx 0{,}0\)) : il est robuste, pas biaisé.
L’optimiseur est trop faible pour résoudre la fonction → \(\Delta\) est du bruit, pas un biais. C’est le Random (control) sur Rastrigin (\(\Delta = 7{,}9\)) : la recherche aléatoire n’atteint jamais l’optimum (meilleur \(\approx 19{-}27\)), donc son \(\Delta\) reflète la variance d’échantillonnage, pas une préférence pour le centre. Sur Sphere et Ackley en revanche, son \(\Delta\) est petit (\(0{,}20\) et \(0{,}12\)) : l’échantillonnage uniforme n’a pas de préférence centrale, donc il obtient un résultat similaire (médiocre, mais semblable) centré ou déplacé.
Leçon. Le biais central n’est ni universel ni absolu : à ce budget, le GA et DE y échappent (ils résolvent ou approchent le problème sans préférence centrale), tandis que les composés métaphoriques (WOA, EO) en sont victimes sur au moins une fonction — FBI, métaphorique lui aussi, y échappe (\(\Delta \le 0{,}002\) partout sur ce banc). Or DE est lui aussi un composé géométrique — même couche que WOA/EO/FBI (GeometricCrossover, MatchMetaHeuristic) — mais son opérateur est une arithmétique nue (\(v = r_1 + F \cdot (r_2 - r_3)\)), sans métaphore. Sa robustesse (\(\Delta \le 0\) partout) écarte donc une cause du biais : ce n’est pas la composition géométrique qui biaise vers le centre. Mais FBI — métaphorique et robuste — écarte la réciproque : la métaphore ne suffit pas à biaiser, c’est l’opérateur qui porte le biais. \(\Delta\) ne se lit pas seul : il faut le confronter à la performance absolue (la valeur centrée). Un \(\Delta\) élevé signe un biais seulement si l’optimiseur résolvait le cas centré ; sinon, il signe juste de la faiblesse. C’est cette lecture croisée — pas le \(\Delta\) brut — qui rend la critique de Kudela (2022) falsifiable.
Confirmer le biais. La signature \(\Delta\) des composés métaphoriques (WOA/Ackley \(4{,}4\), EO/Rastrigin \(2{,}0\)) est partiellement amplifiée par l’amorce « adam au centre » (l’individu initial est à l’optimum exact dans le cas centré) et varie d’une graine à l’autre : voir l’exercice 1 (multi-graine) et l’exercice 2 (amplitude du déplacement) pour vérifier que le biais est structurel, pas un artefact du tirage ou de l’amorce.
Conclusion : le biais central, preuve additionnelle pour « components over metaphors »
MGS-6 montrait qu’aucune métaheuristique « publiée » ne bat systématiquement la composition. MGS-10 ajoute une seconde preuve, plus profonde : le banc centré-vs-déplacé (Kudela 2022) révèle que les composés métaphoriques WOA et EO — reconstruits depuis leurs équations image (bubble-net, équilibre) — sont biaisés vers le centre (\(\bar\Delta\) positif, jusqu’à \(\Delta = 4{,}4\) sur WOA/Ackley), tandis que les assemblages non métaphoriques (GA, DE, BBPSO) sont robustes au déplacement (\(\bar\Delta \le 0\)). FBI, métaphorique lui aussi, échappe au biais sur ce banc (\(\bar\Delta \approx 0\)).
L’apport de DE est décisif. C’est un composé géométrique à part entière — même couche que WOA/EO/FBI (GeometricCrossover, MatchMetaHeuristic, FitnessBasedElitistReinsertion) — mais son opérateur est une arithmétique nue (mutant \(v = r_1 + F \cdot (r_2 - r_3)\)), dénuée de métaphore. Sa robustesse (\(\bar\Delta = -2{,}1\)) écarte une cause du biais : ce n’est pas la composition géométrique qui biaise vers le centre. Mais FBI — composé métaphorique et robuste — écarte la réciproque : la métaphore ne suffit pas à biaiser ; c’est l’opérateur (le bubble-net de WOA, l’équilibre d’EO) qui porte le biais.
La discipline « components over metaphors » sort doublement renforcée : non seulement la métaphore n’apporte rien en performance brute (MGS-6), mais elle cache un biais structurel que seul le protocole centré-vs-déplacé met au jour (MGS-10). Et la règle d’honnêteté tient jusqu’au bout : plutôt que d’invoquer un biais comme argument rhétorique, on le mesure — \(\Delta\) confronté à la performance absolue — pour qu’il cesse d’être invisible.
Exercices
Convention : les exercices sont des cellules à compléter. Squelette fourni, à vous d’implémenter le corps. Les exemples guidés ci-dessus restent des exemples : ne pas les stubber.
Exercice 1 : stabilité de la signature en multi-graine
Le banc ci-dessus tire un seul vecteur de déplacement. La signature \(\Delta\) est-elle stable d’une graine à l’autre, ou un déplacement « chanceux » l’a-t-il gonflée ? Relancez la suite pour plusieurs graines (par ex. 42, 7, 123, 2024) et reportez \(\bar\Delta \pm \sigma\) par optimiseur. En particulier : le \(\Delta = 4{,}3\) de WOA sur Ackley se maintient-il d’une graine à l’autre (biais structurel) ou s’effondre-t-il (artefact) ?
// Exercice 1 : multi-seed (moyenne + écart-type de delta sur N graines).// TODO étudiant : pour chaque graine dans {42, 7, 123, 2024}, lancer RunSuite sur les memes// problemes / optimiseurs, collecter les delta, puis calculer moyenne + ecart-type par optimiseur.Console.WriteLine("Exercice a completer : stabilite multi-graine de la signature de biais.");
Exercice a completer : stabilite multi-graine de la signature de biais.
Exercice 2 : la signature croît-elle avec l’amplitude du déplacement ?
Si le biais est bien « central », un déplacement plus grand devrait produire un \(\Delta\)plus grand (l’optimum sort davantage de la zone que l’optimiseur couvre bien). Balayez shiftMagnitude dans {0.5, 1.0, 2.0, 4.0} et tracez \(\Delta\) moyen par optimiseur en fonction de l’amplitude. Attention : au-delà d’une certaine amplitude, l’optimum approche la frontière du domaine — interprétez alors avec prudence.
// Exercice 2 : balayage de l'amplitude du déplacement.// TODO étudiant : pour shiftMag dans {0.5, 1.0, 2.0, 4.0}, relancer RunSuite et tracer le delta moyen.Console.WriteLine("Exercice a completer : croissance de delta avec l'amplitude du deplacement.");
Exercice a completer : croissance de delta avec l'amplitude du deplacement.
Exercice 3 : étendre le banc à Rosenbrock et Griewank
Ajoutez RosenbrockFitness (vallée plate, optimum en \((1,\dots,1)\) — déjà hors-centre) et GriewankFitness au banc. Comparez : un optimiseur biaisé vers le centre souffre-t-il autant sur Rosenbrock, dont l’optimum n’est pas au centre même dans le cas « centré » ? C’est un contre-exemple instructif : la pathologie mesurée dépend du placement de l’optimum.
// Exercice 3 : étendre la suite de problèmes à Rosenbrock + Griewank.// TODO étudiant : ajouter (new RosenbrockFitness(), dim) et (new GriewankFitness(), dim) à `problems`,// relancer RunSuite, comparer les delta (notamment Rosenbrock, dont l'optimum est hors-centre).Console.WriteLine("Exercice a completer : etendre le banc centre-vs-deplace.");
Exercice a completer : etendre le banc centre-vs-deplace.