Loesungs-Ids wieder als fortlaufende Nummern 1..N (Vorgaenger separat)

- Farm._WurzelId/_KindId ersetzt durch _NeueId: jede Loesung bekommt eine
  einfache fortlaufende Nummer (Wurzeln wie Mutationskinder gleich)
- die Abstammung steckt nicht mehr in der Id, sondern im schon vorher
  gemerkten Vorgaenger (Abstammung-Knoten 'eltern'); der Baum und die
  Hervorhebung des Sieger-Astes funktionieren unveraendert
- Tests auf das neue Id-Schema umgestellt (Nummern 1..N, Abstammung ueber
  Vorgaenger), README/CLAUDE.md aktualisiert
- Beispiele neu berechnet

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-07-11 22:48:49 +02:00
parent 6710d4f06a
commit 740c420900
22 changed files with 3554 additions and 3507 deletions
+22 -29
View File
@@ -330,11 +330,14 @@ class Abstammung:
"""Beobachtungsobjekt: zeichnet den Stammbaum aller Loesungen auf.
Jede Loesung bekommt von der Farm eine fortlaufende Abstammungs-Id.
Beim Erzeugen entsteht ein Wurzelknoten (keine Eltern), beim Mutieren
ein Kindknoten mit Verweis auf die Eltern-Id. alsSVG() zeichnet daraus
einen Baum: Generationen als Zeilen (oben Start, nach unten spaeter),
Knoten als Punkte (Farbe = Wertigkeit), Mutationskanten Eltern->Kind.
Domaenen-agnostisch; funktioniert mit jeder Loesung, die .value hat.
Jede Loesung hat eine fortlaufende Nummer (Id 1..N); die Abstammung
ergibt sich aus dem gemerkten Vorgaenger (Eltern-Id), nicht aus der Id
selbst. Beim Erzeugen entsteht ein Wurzelknoten (keine Eltern), beim
Mutieren ein Kindknoten mit Verweis auf die Eltern-Id. alsSVG() zeichnet
daraus einen Baum: Generationen als Zeilen (oben Start, nach unten
spaeter), Knoten als Punkte (Farbe = Wertigkeit), Mutationskanten
Eltern->Kind. Domaenen-agnostisch; funktioniert mit jeder Loesung, die
.value hat.
"""
def __init__(self):
# je Knoten: Id -> dict(eltern, generation, value, aktion)
@@ -352,9 +355,9 @@ class Abstammung:
"""zeichnet den aufgezeichneten Stammbaum als SVG.
HervorId (optional): Id der hervorzuhebenden Loesung (z.B. der
finalen besten). Ihr Ast wird kraeftig gezeichnet und mit der
sprechenden Id (z.B. 7.2.1) beschriftet, so dass man ihre ganze
Abstammung ablesen kann."""
finalen besten). Ihr Ast (die Kette ihrer Vorgaenger) wird kraeftig
gezeichnet und der Knoten mit seiner Nummer beschriftet, so dass man
die ganze Abstammung im Baum verfolgen kann."""
if not self.Knoten:
raise ValueError("keine Abstammung aufgezeichnet")
@@ -488,9 +491,8 @@ class Farm:
# Abstammung: jede Loesung bekommt eine fortlaufende _absid; die
# aktuelle Generationsnummer waechst mit jedem Erzeugen/Mutieren.
self.Abstammung = Abstammung()
self._AbsZaehler = 0
self._AbsZaehler = 0 # vergibt fortlaufende Loesungs-Ids 1..N
self._Generation = 0
self._KindZaehler = {} # Eltern-Id -> Anzahl bisher vergebener Kinder
if getattr(Zyklus, 'Modus', 'statisch') == 'adaptiv':
self._LaufAdaptiv(Klasse, *Arguments, **keywords)
else:
@@ -570,28 +572,19 @@ class Farm:
print("+ Abbruch: %d Generationen ohne nennenswerte "
"Verbesserung" % OhneVerbesserung)
break
def _WurzelId(self):
"""neue Id fuer eine erzeugte (elternlose) Loesung: schlicht eine
fortlaufende Zahl als String ('1', '2', ...)."""
def _NeueId(self):
"""neue Id fuer eine Loesung: eine fortlaufende Nummer 1..N ueber
alle je erzeugten Loesungen (Wurzeln wie Mutationskinder gleich).
Die Abstammung ergibt sich nicht aus der Id, sondern aus dem
separat gemerkten Vorgaenger (siehe erfassen(..., Eltern))."""
self._AbsZaehler = self._AbsZaehler + 1
return str(self._AbsZaehler)
def _KindId(self, ElternId):
"""neue Id fuer ein Mutationskind: Eltern-Id + '.' + laufender Index,
z.B. '7' -> '7.1', '7.2'; '7.2' -> '7.2.1'. So laesst sich die
ganze Abstammung direkt aus der Id ablesen. Fehlt eine Eltern-Id
(z.B. bei einer Loesung ohne Aufzeichnung), wird eine Wurzel-Id
vergeben."""
if not ElternId:
return self._WurzelId()
self._KindZaehler[ElternId] = self._KindZaehler.get(ElternId, 0) + 1
return "%s.%d" % (ElternId, self._KindZaehler[ElternId])
return self._AbsZaehler
def LoesungenErzeugen(self, N, Klasse, *Arguments, **keywords):
""" Erzeuge Loesungen"""
for i in range(N):
L = Klasse( *Arguments, **keywords )
L._absid = self._WurzelId()
L._absid = self._NeueId() # Wurzel: kein Vorgaenger
self.Abstammung.erfassen(
L._absid, None, self._Generation, getattr(L, 'value', 0), 'e' )
self.Pool.append(L)
@@ -610,10 +603,10 @@ class Farm:
for L in chain(self.Pool):
O = deepcopy(L)
Kind = O.mutieren()
# Kind-Id = Eltern-Id + '.' + laufender Kindindex, damit man
# an der Id die ganze Abstammung ablesen kann (z.B. 7.2.1).
# Kind bekommt eine neue fortlaufende Nummer; der Vorgaenger
# (Eltern-Id) wird separat in der Abstammung gemerkt.
ElternId = getattr(L, '_absid', None)
Kind._absid = self._KindId(ElternId)
Kind._absid = self._NeueId()
self.Abstammung.erfassen(
Kind._absid, ElternId, self._Generation,
getattr(Kind, 'value', 0), 'm' )