MGS-19 — Recuit simulé décomposé : l’opérateur de Metropolis isolé

↑ Partie 4 | ← MGS-18 — Banc CEC

MGS-10 confrontait le recuit simulé (Simulated Annealing) aux autres métaheuristiques comme une boîte noire parmi huit. Mais SA, dans ce fork, n’est pas un monolithe : c’est un composé — SimulatedAnnealing — qui assemble deux ingrédients distincts :

  1. une perturbation gaussienne (le croisement géométrique qui produit un voisin de l’individu courant),
  2. une réinsertion de Metropolis (MetropolisReinsertion) qui accepte ou rejette ce voisin selon le critère \(P = \exp(\Delta / T_k)\).

Le deuxième ingrédient — l’opérateur de Metropolis — vit dans MetropolisReinsertion.cs. Il est installé par défaut ** uniquement** par le compound SA (via SimulatedAnnealing.GetDefaultReinsertion()), et n’est jamais utilisé seul. Ce notebook le débranche de SA et le rebranche seul sur un GA standard (croisement de recombinaison, pas perturbation) : que devient l’acceptation de Metropolis quand on la greffe à l’étage « survie » d’un algorithme génétique ordinaire ?

La thèse est celle qui parcourt toute la Partie 4 — composants > métaphores (Sørensen 2015) — poussée ici jusqu’au démontage du recuit simulé lui-même : SA n’est pas indivisible, son opérateur de survie est réutilisable dans une autre machine. La question falsifiable : l’acceptation exp(Δ/T) à la réinsertion aide-t-elle un GA à échapper aux optima locaux sur un paysage multimodal ? Spoiler honnête — non : le verdict, rapporté ci-dessous chiffres à l’appui, est négatif, et c’est précisément ce résultat négatif qui ferme la leçon.

Le critère de Metropolis et son double habitat

Le critère de Metropolis (Metropolis et al., 1953 ; Kirkpatrick, Gelatt & Vecchi, 1983) accepte un voisin de fitness \(\Delta = f_{\text{enfant}} - f_{\text{parent}} < 0\) (plus mauvais) avec probabilité \(\exp(\Delta / T_k)\) ; un voisin meilleur (\(\Delta \geq 0\)) est toujours accepté. La température suit un schedule géométrique \(T_k = T_0 \, \alpha^k\) : chaud au départ (les montées passent), gelé à la fin (seules les améliorations passent — limite gloutonne).

Dans SA, ce critère est couplé à une perturbation : l’enfant est un voisin gaussien du parent. On l’isole ici en gardant un croisement de recombinaison standard (UniformCrossover) : l’enfant est un recombinant de deux parents, pas un clone perturbé. Le critère de Metropolis s’applique alors à la survie : à chaque paire parent/enfant, on accepte l’enfant (même moins bon) avec la probabilité d’annealing. C’est un hybride « GA + survie annealée », distinct du recuit complet.

Pourquoi le contrôle FitnessBasedPairwiseReinsertion. MetropolisReinsertion apparie parent[i] ↔︎ enfant[i] (pairwise). Pour isoler l’effet du critère d’acceptation, le bon contrôle est donc un pairwise greedy — FitnessBasedPairwiseReinsertion (réutilisé par le compound FBI) — qui garde simplement le meilleur de chaque paire. Même appariement, même croisement/mutation/sélection : seule la règle d’acceptation change. La réinsertion FitnessBasedElitistReinsertion (défaut du GA) opère elle un μ+λ global (top-N sur parents ∪ enfants) : schéma de sélection différent, on la tient pour référence de contexte, pas pour contrôle de l’opérateur.

// Cablage : DLLs MetaGeneticSharp + GeneticSharp + Extensions.
#r "../MetaGeneticSharp/src/MetaGeneticSharp.Domain/bin/Debug/net9.0/GeneticSharp.Infrastructure.Framework.dll"
#r "../MetaGeneticSharp/src/MetaGeneticSharp.Domain/bin/Debug/net9.0/GeneticSharp.Domain.dll"
#r "../MetaGeneticSharp/src/MetaGeneticSharp.Domain/bin/Debug/net9.0/MetaGeneticSharp.Infrastructure.dll"
#r "../MetaGeneticSharp/src/MetaGeneticSharp.Domain/bin/Debug/net9.0/MetaGeneticSharp.Domain.dll"
#r "../MetaGeneticSharp/src/MetaGeneticSharp.Extensions/bin/Debug/net9.0/MetaGeneticSharp.Extensions.dll"

using MetaGeneticSharp;
using GeneticSharp;
using System;
using System.Collections.Generic;
using System.Linq;

Console.WriteLine("DLLs chargees. Operateurs de reinsertion du fork :");
Console.WriteLine("  FitnessBasedElitistReinsertion  (defaut GA, global mu+lambda) : " + typeof(FitnessBasedElitistReinsertion).Name);
Console.WriteLine("  FitnessBasedPairwiseReinsertion (pairwise greedy, compound FBI): " + typeof(FitnessBasedPairwiseReinsertion).Name);
Console.WriteLine("  MetropolisReinsertion           (pairwise + annealing, compound SA) : " + typeof(MetropolisReinsertion).Name);
Console.WriteLine("  SimulatedAnnealing               (compound complet)            : " + typeof(SimulatedAnnealing).Name);
Console.WriteLine("  MetaGeneticAlgorithm .Reinsertion settable       : " + typeof(MetaGeneticAlgorithm).Name);
DLLs chargees. Operateurs de reinsertion du fork :
  FitnessBasedElitistReinsertion  (defaut GA, global mu+lambda) : FitnessBasedElitistReinsertion
  FitnessBasedPairwiseReinsertion (pairwise greedy, compound FBI): FitnessBasedPairwiseReinsertion
  MetropolisReinsertion           (pairwise + annealing, compound SA) : MetropolisReinsertion
  SimulatedAnnealing               (compound complet)            : SimulatedAnnealing
  MetaGeneticAlgorithm .Reinsertion settable       : MetaGeneticAlgorithm
// DoubleArrayChromosome (replique fork, MGS-10/15/16/17/18) : genes double transparents.
public class DoubleArrayChromosome : ChromosomeBase
{
    private readonly double _min; private readonly double _max;
    public DoubleArrayChromosome(double[] values, double min, double max) : base(values.Length)
    { _min=min; _max=max; for (int i=0;i<values.Length;i++) ReplaceGene(i, new Gene(values[i])); }
    public override IChromosome CreateNew()
    { var rand=RandomizationProvider.Current; int n=Length; var v=new double[n];
      for (int i=0;i<n;i++) v[i]=rand.GetDouble(_min,_max); return new DoubleArrayChromosome(v,_min,_max); }
    public override Gene GenerateGene(int geneIndex) => new Gene(RandomizationProvider.Current.GetDouble(_min,_max));
    public double[] GetDoubleValues() => GetGenes().Select(g => (double)g.Value).ToArray();
}

const int PopSize = 50;
const int NFE = 5000;          // budget commun a toutes les configs (pas de triche par le budget)
const int dim = 5;             // dimension ou les operateurs differentient (cf MGS-11/14)
int[] seeds = { 7, 42, 99 };   // multi-seed (meme convention que MGS-18)

// GA plain dont on n'echange QUE l'operateur de reinsertion. Tout le reste -- croisement
// (UniformCrossover), mutation (UniformMutation), selection (EliteSelection), metaheuristic
// (DefaultMetaHeuristic = pass-through GA classique) -- est identique d'une config a l'autre.
// L'effet mesure est donc PUREMENT celui de la regle de survie.
Optimizer MakeGAWithReinsertion(IReinsertion reins)
{ return (Optimizer)((req) => { int gens=Math.Max(1,req.Evaluations/PopSize);
    var (min,max)=req.Bounds; double mid=0.5*(min+max);
    var adam=new DoubleArrayChromosome(Enumerable.Repeat(mid,req.Dimension).ToArray(),min,max);
    var pop=new MetaPopulation(PopSize,PopSize,adam);
    var ga=new MetaGeneticAlgorithm(pop,req.Fitness,new EliteSelection(),new UniformCrossover(0.5f),new UniformMutation(true),new DefaultMetaHeuristic());
    ga.Reinsertion = reins;                                  // <-- injection de l'operateur isole
    ga.Termination=new GenerationNumberTermination(gens); ga.Start();
    return ga.BestChromosome.Fitness ?? double.NegativeInfinity; }); }

// Objectif moyen sur les graines (objectif = -fitness, proche 0 = meilleur ; le RNG est reseede
// avant chaque course, donc les chiffres committes sont reproduisibles).
double RunAvg(Optimizer opt, IFitness fit)
{ var bounds = KnownFunctionsBounds.For(fit.GetType());
  double s=0; foreach(var sd in seeds){ FastRandomRandomization.ResetSeed(sd);
    s += -opt(new OptimizerRequest(fit, bounds, dim, NFE)); }
  return s/seeds.Length; }

// Les 5 configurations : Pairwise et Metropolis sont pairwise (memes paires) -- la comparaison
// isole le critere d'acceptation. Elitist (mu+lambda global) = reference de contexte.
IReinsertion Pairwise       = new FitnessBasedPairwiseReinsertion();   // controle greedy pairwise
IReinsertion MetropolisCold = new MetropolisReinsertion(0.2, 0.90);    // T0 faible, gele vite ~ greedy
IReinsertion MetropolisMed  = new MetropolisReinsertion(1.0, 0.95);    // T0 moyen, refroidissement moyen
IReinsertion MetropolisHot  = new MetropolisReinsertion(5.0, 0.99);    // T0 eleve, refroidissement lent
IReinsertion Elitist        = new FitnessBasedElitistReinsertion();    // reference : defaut GA global

FastRandomRandomization.ResetSeed(42);
double smoke = -MakeGAWithReinsertion(Pairwise)(new OptimizerRequest(new SphereFitness(), KnownFunctionsBounds.For(typeof(SphereFitness)), dim, NFE));
Console.WriteLine($"Setup OK. PopSize={PopSize}, NFE={NFE} (gens={NFE/PopSize}), dim={dim}, seeds={string.Join(", ",seeds)}.");
Console.WriteLine($"Smoke GA+Pairwise/Sphere objectif={smoke:F3} (proche 0 = OK).");
Setup OK. PopSize=50, NFE=5000 (gens=100), dim=5, seeds=7, 42, 99.
Smoke GA+Pairwise/Sphere objectif=0,017 (proche 0 = OK).

Méthode

Cinq configurations, un budget commun (NFE = 5000, dimension 5, moyenne 3 graines) :

Reinsertion Appariement Règle d’acceptation Rôle
FitnessBasedPairwiseReinsertion pairwise greedy (meilleur de la paire) contrôle
MetropolisReinsertion(0,2 ; 0,90) pairwise exp(Δ/T), T faible froid ≈ greedy
MetropolisReinsertion(1,0 ; 0,95) pairwise exp(Δ/T), T moyen annealing modéré
MetropolisReinsertion(5,0 ; 0,99) pairwise exp(Δ/T), T élevé, lent annealing marqué
FitnessBasedElitistReinsertion μ+λ global top-N référence contexte

Les trois Metropolis partagent le même appariement que le contrôle Pairwise ; la différence est uniquement la règle d’acceptation (greedy → annealing). C’est cette comparaison qui isole l’opérateur. Le banc est seedé (FastRandomRandomization.ResetSeed avant chaque course) : les chiffres sont reproduisibles.

Partie A — le témoin neutre : Sphere (unimodale)

Sur Sphere (unimodale, lisse), tout déplacement qui n’améliore pas la fitness est une montée inutile : il n’y a pas d’optimum local à franchir. La prédiction de la théorie : l’acceptation de Metropolis n’a rien à accepter d’utile — accepter une montée ne fait que bruiter la convergence. Les Metropolis froid/moyen doivent donc égaliser le contrôle greedy, et le réglage chaud (beaucoup d’annealing) le dégrader (beaucoup de bruit). C’est notre témoin neutre : un problème où l’opérateur, par construction, ne peut pas aider.

void RunBanc(IFitness fit, string fname)
{
    Console.WriteLine($"\n===== {fname} (dim={dim}, NFE={NFE}, moy 3 seeds) -- objectif (proche 0 = meilleur) =====");
    Console.WriteLine($"{"Reinsertion",-32}|{"objectif",10}");
    Console.WriteLine(new string('-',46));
    Console.WriteLine($"{"Pairwise greedy (controle)",-32}|{RunAvg(MakeGAWithReinsertion(Pairwise), fit),10:F3}");
    Console.WriteLine($"{"Metropolis T0=0.2 a=0.90 (froid)",-32}|{RunAvg(MakeGAWithReinsertion(MetropolisCold), fit),10:F3}");
    Console.WriteLine($"{"Metropolis T0=1.0 a=0.95 (moyen)",-32}|{RunAvg(MakeGAWithReinsertion(MetropolisMed), fit),10:F3}");
    Console.WriteLine($"{"Metropolis T0=5.0 a=0.99 (chaud)",-32}|{RunAvg(MakeGAWithReinsertion(MetropolisHot), fit),10:F3}");
    Console.WriteLine($"{"Elitist global (reference)",-32}|{RunAvg(MakeGAWithReinsertion(Elitist), fit),10:F3}");
}
RunBanc(new SphereFitness(), "Sphere");

===== Sphere (dim=5, NFE=5000, moy 3 seeds) -- objectif (proche 0 = meilleur) =====
Reinsertion                     |  objectif
----------------------------------------------
Pairwise greedy (controle)      |     0,012
Metropolis T0=0.2 a=0.90 (froid)|     0,014
Metropolis T0=1.0 a=0.95 (moyen)|     0,016
Metropolis T0=5.0 a=0.99 (chaud)|     0,099
Elitist global (reference)      |     0,011

Lecture (G.9). Sur Sphere, le contrôle greedy et les Metropolis froid/moyen résolvent la fonction (objectif ≈ 0,01) : il n’y a pas de montée utile à accepter — c’est le témoin neutre attendu. Le réglage chaud dégrade pourtant nettement (≈ 0,10, soit ~8× le contrôle) : à force d’accepter des montées inutiles, il injecte du bruit dans une convergence facile. Leçon provisoire : sur un paysage unimodal, l’annealing ne sert à rien et trop d’annealing nuit. Le paysage multimodal dira si cela s’inverse.

Partie B — le paysage discriminant : Rastrigin (fortement multimodale)

Rastrigin (\(\sum [x_i^2 - 10\cos(2\pi x_i)] + 10n\)) est une forêt régulière d’optima locaux séparés par des montées de fitness. Hypothèse naturelle à éprouver : un GA greedy pairwise se fige dans le premier bassin rencontré, tandis qu’un GA à survie annealée accepte, tant que T est chaud, des enfants moins bons — donc maintient la diversité et pourrait franchir les cols entre bassins. La question n’est pas de savoir si Rastrigin est résolu (à NFE = 5000 en dimension 5, aucun des opérateurs ne l’annule) mais quel critère d’acceptation atteint le meilleur bassin. Le tableau ci-dessous tranche — et il tranche contre l’hypothèse.

RunBanc(new RastriginFitness(), "Rastrigin");

===== Rastrigin (dim=5, NFE=5000, moy 3 seeds) -- objectif (proche 0 = meilleur) =====
Reinsertion                     |  objectif
----------------------------------------------
Pairwise greedy (controle)      |     1,190
Metropolis T0=0.2 a=0.90 (froid)|     1,624
Metropolis T0=1.0 a=0.95 (moyen)|     1,229
Metropolis T0=5.0 a=0.99 (chaud)|     1,771
Elitist global (reference)      |     0,385

Lecture honnête (résultat négatif). Le verdict réfute l’hypothèse d’évasion : sur Rastrigin, aucun réglage Metropolis n’améliore le contrôle greedy pairwise (1,19) — le froid (1,62) et le chaud (1,77) le dégradent nettement, le moyen (1,23) est dans le bruit. Accepter des enfants moins bons à la réinsertion n’aide pas le GA à franchir les cols ; cela l’écarte des bons bassins, et le réglage le plus chaud (le plus d’annealing) est le pire.

Pourquoi l’évasion échoue. L’explication la plus naturelle tient à la sémantique du « voisin ». Dans SA, l’enfant est un voisin du parent (perturbation gaussienne isotrope) : accepter un voisin moins bon a le sens d’une exploration du voisinage. Ici l’enfant est un recombinant de deux parents (mosaïque par gène via UniformCrossover) : il n’est le voisin ni de l’un ni de l’autre. Accepter un recombinant moins bon n’est donc pas une exploration de voisinage — c’est du bruit injecté à la survie. Le bénéfice du recuit réside dans le couplage perturbation+acceptation, pas dans l’acceptation seule. (L’élitiste global μ+λ, référence 0,39, gagne encore : la sélection globale top-N est plus exploitative que tout schéma pairwise — mais ce n’est pas l’objet ; l’objet est la comparaison Metropolis-vs-Pairwise à appariement constant, et elle est négative.)

Partie C — second multimodal : Ackley (puits central + rides fines)

Ackley est l’autre paysage multimodal canonique : un grand puits central entouré de rides cosinus à période fine. On y reporte le même banc pour vérifier si le verdict négatif de Rastrigin se confirme ou si Ackley, à la géométrie différente (large puits plutôt que forêt régulière), répond autrement.

RunBanc(new AckleyFitness(), "Ackley");

===== Ackley (dim=5, NFE=5000, moy 3 seeds) -- objectif (proche 0 = meilleur) =====
Reinsertion                     |  objectif
----------------------------------------------
Pairwise greedy (controle)      |     2,743
Metropolis T0=0.2 a=0.90 (froid)|     2,455
Metropolis T0=1.0 a=0.95 (moyen)|     2,918
Metropolis T0=5.0 a=0.99 (chaud)|     4,320
Elitist global (reference)      |     2,000

Lecture. Même verdict qu’en Rastrigin : le Metropolis froid (2,46) est dans le bruit du contrôle greedy (2,74) — pas d’amélioration fiable ; le moyen (2,92) et surtout le chaud (4,32) dégradent. La géométrie différente d’Ackley ne renverse pas la conclusion : l’acceptation annealée détachée du couplage SA n’aide pas.

Partie D — la signature de température et la limite frozen

L’effet de MetropolisReinsertion est piloté par le schedule \(T_k = T_0 \alpha^k\). On le trace sur 100 générations (= NFE/PopSize) : on voit le spectre chaud persistant → gélification rapide. La limite frozen (\(T \to 0\)) est l’autre vérification de cohérence : quand T gèle, le critère exp(Δ/T) devient 0 pour toute montée — la règle d’acceptation dégénère en greedy. On la vérifie sur le témoin lisse Sphere : là, même si l’opérateur consomme un tirage RNG par évaluation (et diverge donc de la trajectoire du contrôle), la convergence lisse ramène les deux au même lieu. (Sur un paysage rude comme Rastrigin, la divergence de trajectoire domine et l’égalité numérique n’a plus de sens — d’où le choix du témoin lisse.)

// Trace du cooling schedule T_k = T0 * alpha^k sur 100 generations.
Console.WriteLine("Cooling schedule T_k = T0 * alpha^k (k = generation, sur 100 gens) :\n");
Console.WriteLine($"{"k",4}|{"T0=0.2 a=0.90",14}|{"T0=1.0 a=0.95",14}|{"T0=5.0 a=0.99",14}");
Console.WriteLine(new string('-',50));
var sched = new[] { (0.2,0.90), (1.0,0.95), (5.0,0.99) };
for (int k=0; k<=100; k+=10)
{ var row = sched.Select(s => Math.Pow(s.Item2,k) * s.Item1);
  Console.WriteLine($"{k,4}|{row.ElementAt(0),14:E3}|{row.ElementAt(1),14:E3}|{row.ElementAt(2),14:E3}"); }

// Limite frozen : T0 quasi-nul => exp(delta/T)->0 pour toute montee => la REGLE d'acceptation
// devient greedy. Verifie sur Sphere (temoin lisse) -- la ou la divergence de trajectoire RNG
// (l'operateur consomme un tirage par evaluation, meme en rejetant) ne masque pas l'effet de la regle.
IReinsertion MetropolisFrozen = new MetropolisReinsertion(1e-9, 0.50);
double frozenSphere = RunAvg(MakeGAWithReinsertion(MetropolisFrozen), new SphereFitness());
double pairSphere   = RunAvg(MakeGAWithReinsertion(Pairwise), new SphereFitness());
Console.WriteLine("\nLimite frozen (T0=1e-9) sur Sphere (temoin lisse) : la REGLE devient greedy :");
Console.WriteLine($"  Metropolis frozen = {frozenSphere:F4}");
Console.WriteLine($"  Pairwise greedy   = {pairSphere:F4}");
Console.WriteLine($"  Ecart |frozen - pairwise| = {Math.Abs(frozenSphere-pairSphere):F4} (proche = regle greedy confirmee ; residu = divergence trajectoire RNG).");
Cooling schedule T_k = T0 * alpha^k (k = generation, sur 100 gens) :

   k| T0=0.2 a=0.90| T0=1.0 a=0.95| T0=5.0 a=0.99
--------------------------------------------------
   0|    2,000E-001|    1,000E+000|    5,000E+000
  10|    6,974E-002|    5,987E-001|    4,522E+000
  20|    2,432E-002|    3,585E-001|    4,090E+000
  30|    8,478E-003|    2,146E-001|    3,699E+000
  40|    2,956E-003|    1,285E-001|    3,345E+000
  50|    1,031E-003|    7,694E-002|    3,025E+000
  60|    3,594E-004|    4,607E-002|    2,736E+000
  70|    1,253E-004|    2,758E-002|    2,474E+000
  80|    4,369E-005|    1,652E-002|    2,238E+000
  90|    1,524E-005|    9,888E-003|    2,024E+000
 100|    5,312E-006|    5,921E-003|    1,830E+000

Limite frozen (T0=1e-9) sur Sphere (temoin lisse) : la REGLE devient greedy :
  Metropolis frozen = 0,0197
  Pairwise greedy   = 0,0124
  Ecart |frozen - pairwise| = 0,0073 (proche = regle greedy confirmee ; residu = divergence trajectoire RNG).

Synthèse — un opérateur démonté, un bénéfice qui ne voyage pas

Trois leçons se dégagent, toutes honnêtes (G.9) :

  1. Le recuit simulé est démontable. SimulatedAnnealing n’est pas un bloc : c’est un assemblage (perturbation gaussienne + MetropolisReinsertion). On peut débrancher l’opérateur de Metropolis et le rebrancher seul sur un GA de recombinaison — il fonctionne, il s’exécute sur le vrai moteur du fork, et son effet est mesurable. C’est la thèse « composants > métaphores » (Sørensen 2015) appliquée au recuit lui-même : la métaphore thermodynamique n’est pas indivisible.

  2. L’opérateur discrimine sur le multimodal — mais l’effet est négatif. Sur Sphere (unimodal), l’annealing ne sert à rien (témoin neutre) et le réglage chaud dégrade (≈ 0,10 contre ≈ 0,01). Sur Rastrigin et Ackley (multimodal), le Metropolis pairwise n’apporte jamais d’amélioration fiable au contrôle greedy pairwise — sur Ackley le froid affiche 2,46 contre 2,74 au contrôle, mais l’écart reste dans le bruit d’une moyenne de trois graines (et aucun réglage ne passe sous le contrôle sur Rastrigin) ; le réglage le plus chaud est le pire des deux paysages. Le critère exp(Δ/T) change bien le comportement (Prong-B satisfait : l’opérateur n’équivaut pas à une baseline sur le multimodal, la sortie le montre), mais pas dans le sens espéré : accepter des recombinants moins bons n’évade pas les optima locaux, c’est du bruit.

  3. Le bénéfice du recuit est dans le couplage, pas dans l’acceptation seule. SA marche parce que la perturbation propose un voisin du courant et l’acceptation anneale entre voisins ; les deux sont conjointement accordés. Détacher l’acceptation et la brancher sur un croisement de recombinaison (l’enfant = mosaïque de deux parents, pas un voisin) brise cette sémantique. C’est la leçon « composants > métaphores » dans sa forme la plus exigeante : on PEUT démonter SA, l’opérateur isolé FONCTIONNE sur le vrai moteur — mais il ne porte pas, à lui seul, le bénéfice du tout. Le tout est plus que la somme des pièces, et c’est précisément en démontant qu’on le prouve (écho de la synergie conditionnelle de MGS-11/14 : la composition n’est magique ni assemblée ni désassemblée).

Le prolongement naturel n’en reste pas moins la composition : ayant isolé MetropolisReinsertion, on peut le recombiner ailleurs qu’en survie brute de GA — par exemple l’injecter comme étage d’exploration dans une île d’un archipel (MGS-4/11), où la diversité qu’il entretient pourrait payer autrement. L’opérateur, démonté, reste une brique disponible ; ce notebook dit seulement où il ne suffit pas.

Exercices

  1. Schedule lent vs rapide. Ajoutez deux réglages extrêmes : T0=10, α=0.999 (très lent, quasi-constant) et T0=1, α=0.5 (gélification en ~5 générations). Reportez-les sur Rastrigin : un schedule trop lent maintient-il trop de diversité (pas de convergence) ? Confirme-t-il la monotonie « plus d’annealing = plus de dégradation » observée ?
  2. Recombiner la perturbation. L’hypothèse mécanistique avancée (l’enfant recombinant n’est pas un « voisin ») est testable : remplacez le UniformCrossover par la perturbation gaussienne de SA (SimulatedAnnealing.DefaultPerturbationOperator comme croisement géométrique) tout en gardant MetropolisReinsertion. Si l’hypothèse est juste, l’opérateur de Metropolis redevient utile (on a reconstitué le couplage). Mesurez sur Ackley.
  3. Composition insulaire. Injectez MetropolisReinsertion dans une seule île d’un archipel hétérogène (une île « annealée » + des îles DE/BBPSO, cf MGS-11/14). La diversité entretenue par l’annealing paie-t-elle cette fois au niveau de l’archipel ? Concevez le banc (même migration, ratio variable) et rapportez le verdict.

Pour aller plus loin

  • Le compound complet : SimulatedAnnealing.cs sur le fork montre comment perturbation + MetropolisReinsertion se couplent dans le recuit original.
  • MGS-10 (biais central) confrontait SA à sept autres optimiseurs comme boîtes noires ; ce notebook ouvre la boîte et montre que l’opérateur de survie, isolé, ne porte pas le bénéfice.
  • MGS-17 (parameter control) donne le cadre du No-Free-Lunch au niveau du paramètre ; la leçon 2 ci-dessus est son exact analogue au niveau de l’opérateur.
  • MGS-18 (banc CEC) est le protocole standard pour éprouver ces opérateurs sur les paysages shift+rotate combinés.

Référence

Métaheuristique Référence
Critère d’acceptation de Metropolis (recuit simulé) Metropolis, N., Rosenbluth, A. W., Rosenbluth, M. N., Teller, A. H., & Teller, E. (1953) — « Equation of State Calculations by Fast Computing Machines », Journal of Chemical Physics 21(6).
Recuit simulé en optimisation Kirkpatrick, S., Gelatt, C. D., & Vecchi, M. P. (1983) — « Optimization by Simulated Annealing », Science 220(4598).
Métaheuristiques = composants (pas métaphores) Sørensen, K. (2015) — « Metaheuristics—the metaphor exposed », Intl. Trans. in Operational Research 22(1). doi:10.1111/itor.12001

7. Exercices structures

Trois exercices completables directement dans le notebook (cellule code stub C.1-compliant). Chaque stub s’execute tel quel (affiche Exercice N a completer) et laisse l’etudiant completer le # TODO etudiant correspondant. Les questions de reflexion textuelles de la section 6 ci-dessus sont preservees (Consolider != Archiver) ; cette section ajoute la version structurée (header ### Exercice N : + cellule code stub) requise par la convention #2161 (3 exercices / notebook).

Exercice 1 : Schedule lent vs rapide sur Rastrigin

Question de reflexion (cf. section 6 item 1) : Ajoutez deux reglages extremes : T0=10, alpha=0.999 (très lent, quasi-constant) et T0=1, alpha=0.5 (gelification en ~5 generations). Reportez-les sur Rastrigin : un schedule trop lent maintient-il trop de diversite (pas de convergence) ? Confirme-t-il la monotonie « plus d’annealing = plus de degradation » observee ?

// Exercice 1 : Schedule lent vs rapide sur Rastrigin.
// Comparez trois settings de cooling schedule T_k = T0 * alpha^k :
//   - reference : T0=0.2, alpha=0.90 (celui de la section 4)
//   - lent      : T0=10,  alpha=0.999 (quasi-constant, pas de convergence)
//   - rapide    : T0=1,   alpha=0.5   (gelification en ~5 generations)
//
// Pour chacun, lancez N seeds et mesurez le best fitness final sur Rastrigin 2D.
// Verdict attendu : monotone decroissant du taux de reussite quand T0->0 (gelifie).
//
// AIDE : la cellule peut reutiliser `MetropolisReinsertion`, `rastriginFn`, le rng,
// et le squelette de boucle de la cellule 11 (Run + best). Conserver le meme budget
// NFE = Pop * Gens = 30 * 30 = 900 evaluations.

Console.WriteLine("Exercice 1 a completer — implementer le tableau 3 schedules x N seeds");
// TODO etudiant :
//   var schedules = new (string label, double T0, double alpha)[] {
//       ("reference", 0.2,  0.90),
//       ("lent",      10.0, 0.999),
//       ("rapide",    1.0,  0.5),
//   };
//   foreach (var (label, T0, alpha) in schedules) {
//       var successes = 0;
//       for (int seed = 0; seed < Seeds; seed++) {
//           var rng = new System.Random(seed);
//           // ... appel a Run avec T0, alpha modifies
//           // ... increment successes si best fitness < seuil
//       }
//       Console.WriteLine($"{label,-12} T0={T0,-5} alpha={alpha,-5} : {successes}/{Seeds}");
//   }
Exercice 1 a completer — implementer le tableau 3 schedules x N seeds

Exercice 2 : Recombiner la perturbation sur Ackley

Question de reflexion (cf. section 6 item 2) : L’hypothese mecanistique « l’enfant recombinant n’est pas un voisin » est testable : remplacez le UniformCrossover par la perturbation gaussienne de SA (SimulatedAnnealing.DefaultPerturbationOperator comme croisement geometrique) tout en gardant MetropolisReinsertion. Si l’hypothese est juste, l’opérateur de Metropolis redevient utile (on a reconstitue le couplage). Mesurez sur Ackley.

// Exercice 2 : Recombiner la perturbation sur Ackley.
// Test falsifiable : remplacez le crossover Uniform par la perturbation gaussienne
// de SimulatedAnnealing (vue comme croisement geometrique), et gardez
// MetropolisReinsertion comme operateur de reinsertion.
//
// Paysage : Ackley 2D (cf. KnownFunctions du submodule, plus multimodal que Rastrigin).
// Prediction : si l'hypothese « le couplage perturbation+Metropolis est utile » est juste,
// le crossover gaussien + Metropolis doit produire des resultats meilleurs que le crossover
// Uniform + Metropolis (qui est le cas demontre dans la cellule 11 = « benefice ne voyage pas »).
//
// AIDE : Definissez un crossover custom qui prend 2 parents et retourne
//   child[i] = (parent1[i] + parent2[i])/2 + N(0, sigma)
// avec sigma = 0.1 (meme ordre que la perturbation gaussienne de SA).
//

Console.WriteLine("Exercice 2 a completer — crossover gaussien + Metropolis sur Ackley");
// TODO etudiant :
//   var ackleyFn = new Ackley2D();  // ou equivalent depuis KnownFunctions
//   var customCrossover = new GaussianCrossover(sigma: 0.1);
//   for (int seed = 0; seed < Seeds; seed++) {
//       var (u, v, best) = Run(ackleyFn, customCrossover, MetropolisReinsertion,
//                              rng: new System.Random(seed),
//                              pop: 30, gens: 30);
//       // reporter best fitness
//   }
Exercice 2 a completer — crossover gaussien + Metropolis sur Ackley

Exercice 3 : Composition insulaire (heterogene)

Question de reflexion (cf. section 6 item 3) : Injectez MetropolisReinsertion dans une seule ile d’un archipel heterogene (une ile « annealee » + des iles DE/BBPSO, cf MGS-11/14). La diversite entretenue par l’annealing paie-t-elle cette fois au niveau de l’archipel ? Concevez le banc (même migration, ratio variable) et rapportez le verdict.

// Exercice 3 : Composition insulaire heterogene.
// Test : 4 iles heterogenes + migration periodique, mesurer si UNE iles « annealee »
// (crossover Uniform + MetropolisReinsertion) ameliore l'archipel par rapport a
// un archipel 100 % Uniform ou 100 % DE.
//
// AIDE : reprendre le pattern `Island` + `MigrationTopology` du submodule
// (cf. MGS-04-Islands, MGS-11-IslandSynergy, MGS-14-IslandSynergyFound).
// Configurations a comparer (meme budget total NFE = 1200) :
//   - archi A : 4 iles DE (baseline)
//   - archi B : 4 iles Uniform (baseline)
//   - archi C : 1 ile Annealee + 3 iles DE (test)
//   - archi D : 1 ile Annealee + 3 iles Uniform (test)
//

Console.WriteLine("Exercice 3 a completer — banc archipel heterogene 4 configs x N seeds");
// TODO etudiant :
//   var configs = new[] {
//       ("A_DE_4",         new (int n, ICrossover cx, IReinsertion re)[] { ... }),
//       ("B_Uniform_4",    ...),
//       ("C_Anneal+DE_3",  ...),
//       ("D_Anneal+Unif_3",...),
//   };
//   foreach (var (label, iles) in configs) {
//       // ... boucle seeds, mesure best fitness final de l'archipel
//   }
Exercice 3 a completer — banc archipel heterogene 4 configs x N seeds
Retour au sommet