Adaptiver GA-Zyklus, Pool-Groessen-Panel, Konfig direkt aus cfg-Dateien
Zyklus/Farm: - neuer adaptiver Modus: grosse Startpopulation, die je Generation schrumpft (Start/Schrumpfung/MinPopulation) und bei Konvergenz stoppt (Geduld Generationen ohne Verbesserung >= Schwelle, max MaxGenerationen) - Zyklus.laden erkennt adaptives Profil am Feld 'Start' (Modus adaptiv), sonst klassisch (Abfolge/Anzahl); Farm._LaufStatisch/_LaufAdaptiv - neue Trunkierungs-Selektion Farm.PopulationBegrenzen - [Adaptiv]-Profil in zyklus.cfg (dokumentiert) Entwicklungsgrafik: - zweites Panel unter dem Verlauf zeigt die Anzahl erzeugter Verteilungen im Pool je Schritt (gemeinsame Zeitachse) Konfiguration: - platz.cfg entfernt; zyklus.cfg/strafen.cfg werden direkt aus $PLATZ_CFG geladen - neuer Schalter --zyklus-art (Default Adaptiv) waehlt das GA-Profil - web/main.py analog: direktes Laden, Profil via PLATZ_ZYKLUSART Doku (README.md, CLAUDE.md) und Tests entsprechend aktualisiert; Beispiele mit dem Adaptiv-Default neu berechnet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,7 @@ import unittest
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'libs'))
|
||||
|
||||
from Strukturdaten import Gruppe, Gruppen, JSONConfig, Person, Sitzplatzverteilung, Strafliste, Tisch, Tische
|
||||
from ga import Entwicklung, _StringAusdruck
|
||||
from ga import Entwicklung, Farm, Zyklus, _StringAusdruck
|
||||
|
||||
IMAGES_DIR = os.path.join(os.path.dirname(__file__), '..', 'doc', 'images')
|
||||
|
||||
@@ -267,12 +267,65 @@ class TestEntwicklung(unittest.TestCase):
|
||||
with open(Pfad) as f:
|
||||
svg = f.read()
|
||||
self.assertIn( '<svg', svg )
|
||||
self.assertEqual( svg.count('<polyline'), 3 ) # best/schnitt/schlecht
|
||||
# 3 Wertigkeitskurven (best/schnitt/schlecht) + 1 Pool-Groessenkurve
|
||||
self.assertEqual( svg.count('<polyline'), 4 )
|
||||
# das zweite Panel zeigt die Anzahl erzeugter Verteilungen im Pool
|
||||
self.assertIn( 'Anzahl erzeugter Verteilungen im Pool', svg )
|
||||
self.assertIn( 'stroke="#4060a0"', svg ) # Pool-Kurve
|
||||
|
||||
def test_leere_entwicklung_wirft_beim_svg(self):
|
||||
with self.assertRaises(ValueError):
|
||||
Entwicklung().alsSVG( os.path.join(IMAGES_DIR, 'leer.svg') )
|
||||
|
||||
|
||||
class TestAdaptiverZyklus(unittest.TestCase):
|
||||
"""Der adaptive Zyklus startet mit vielen Loesungen, laesst die
|
||||
Population schrumpfen und bricht bei Konvergenz ab."""
|
||||
|
||||
class _L:
|
||||
"""minimale GA-Loesung: fester Wert, Mutation aendert nichts (so
|
||||
konvergiert der beste Wert sofort -> Abbruch nach 'Geduld')."""
|
||||
def __init__(self, *a, **k):
|
||||
self.value = -100
|
||||
def mutieren(self):
|
||||
return self
|
||||
def __eq__(self, other):
|
||||
return self.value == other.value
|
||||
def __lt__(self, other):
|
||||
return self.value < other.value
|
||||
|
||||
def _zyklus(self, **params):
|
||||
Z = Zyklus()
|
||||
Z.Modus = 'adaptiv'
|
||||
Z.Adaptiv = dict(Zyklus.AdaptivDefaults)
|
||||
Z.Adaptiv.update(params)
|
||||
return Z
|
||||
|
||||
def test_population_schrumpft_und_bricht_bei_konvergenz_ab(self):
|
||||
Z = self._zyklus( Start=40, Schrumpfung=0.85, MinPopulation=5,
|
||||
MaxGenerationen=50, Geduld=3, Schwelle=1.0 )
|
||||
F = Farm( Z, self._L )
|
||||
|
||||
# Pool-Groessen je 's'-Aufzeichnung (Ende jeder Generation)
|
||||
pop_pro_gen = [ p[5] for p in F.Entwicklung.Punkte if p[1] == 's' ]
|
||||
# monoton fallend (Population schrumpft) ...
|
||||
self.assertTrue( all( a >= b for a, b in zip(pop_pro_gen, pop_pro_gen[1:]) ),
|
||||
"Population muss von Generation zu Generation schrumpfen: %s"
|
||||
% pop_pro_gen )
|
||||
self.assertLess( pop_pro_gen[-1], pop_pro_gen[0] )
|
||||
# ... und da nichts besser wird, nach Geduld(=3) Generationen Abbruch,
|
||||
# also klar vor MaxGenerationen(=50).
|
||||
self.assertLessEqual( len(pop_pro_gen), 4 )
|
||||
|
||||
def test_population_faellt_nicht_unter_min(self):
|
||||
Z = self._zyklus( Start=8, Schrumpfung=0.5, MinPopulation=5,
|
||||
MaxGenerationen=10, Geduld=99, Schwelle=1.0 )
|
||||
F = Farm( Z, self._L )
|
||||
pop_pro_gen = [ p[5] for p in F.Entwicklung.Punkte if p[1] == 's' ]
|
||||
self.assertTrue( all( n >= 5 for n in pop_pro_gen ),
|
||||
"MinPopulation=5 darf nicht unterschritten werden: %s"
|
||||
% pop_pro_gen )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user