feat: Günstigster Pfad auf der Dependency Closure — exakt über gekoppelte Gruppen (SPEC §9, D42)
Jeder nötige Knoten zieht seine :#…-Ziele samt Realisierung in die
nötige Menge; gemeinsam Gebrauchtes zählt über die Vereinigung nur
einmal. Verfahren gewählt und benannt (D34): erschöpfende Suche, aber
nur über die GEKOPPELTEN Gruppen (Teilbaum enthält Abhängigkeiten oder
wird gebraucht) — ungekoppelte wählen lokal wie bisher, ohne
Abhängigkeiten bleibt es bei genau einer Auswertung zum alten Preis.
Lexikografischer Odometer, strikt < : bei Gleichstand die erste
Alternative. Oberhalb des Suchlimits (20 000) gierig mit zeilenloser
Warnung cheapApprox statt stillschweigender Pseudo-Optimalität.
Abhängigkeiten ziehen auch optionale und nicht gewählte Ziele (ohne
deren Vorfahren), verworfene nie. Neue API computeCheapPlan {set,exact};
markCheapest entfällt. 8 neue Tests (tests/closure.test.js).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
afe536e33c
commit
a918c75286
@@ -1985,3 +1985,44 @@ Kante — ein Kreis von einem Knoten zu sich selbst sagte nichts.
|
||||
auf dem DOM; headless getestet sind die `data-id`/`data-deps`-Attribute des
|
||||
Renderers, geprüft wurde im Browser (alle drei Modi, Fokus- und
|
||||
Cursor-Hervorhebung, Export).
|
||||
|
||||
## D42 — Closure-Pfad: erschöpfend über die gekoppelten Gruppen, gierig nur benannt
|
||||
Die letzte Baustelle aus D34: Mit Abhängigkeiten zählt der günstigste Pfad
|
||||
nicht mehr den gewählten Teilbaum, sondern die **Hülle** — jeder nötige Knoten
|
||||
zieht seine Ziele samt Realisierung nach, gemeinsam Gebrauchtes zählt über die
|
||||
Mengen-Vereinigung nur einmal. Damit ist die Wahl je Alternativgruppe nicht
|
||||
mehr lokal optimal (das D34-Beispiel: `A (S) :#db` schlägt `B (M)`, sobald
|
||||
`#db` ohnehin bezahlt wird). Die Entscheidungen:
|
||||
|
||||
**Verfahren: erschöpfende Suche — aber nur über die gekoppelten Gruppen.**
|
||||
D34 stellte „exakt, die Bäume sind klein“ gegen „gierig, aber benannt“;
|
||||
gebaut sind beide, mit einer Beobachtung dazwischen, die die exakte Suche
|
||||
praktisch immer billig macht: **Nur Gruppen, in deren Teilbäumen
|
||||
Abhängigkeiten stehen oder auf deren Knoten welche zeigen, koppeln
|
||||
überhaupt.** Alle übrigen wählen weiterhin lokal (kleinste rekursive Kosten,
|
||||
Gleichstand ⇒ erste) — ohne Abhängigkeiten gibt es null gekoppelte Gruppen
|
||||
und genau eine Auswertung, also exakt das alte Verhalten zum alten Preis.
|
||||
Über die gekoppelten Gruppen läuft ein lexikografischer Odometer (frühe
|
||||
Gruppen wechseln zuletzt, strikt `<` gewinnt — bei Gleichstand bleibt so die
|
||||
erste Alternative, §9). Übersteigt das Produkt der Gruppengrößen das
|
||||
Suchlimit (20 000), fällt die Rechnung auf die gierige lokale Wahl zurück
|
||||
und **sagt es**: zeilenlose Warnung `cheapApprox` — die in D34 verlangte
|
||||
Benennung, statt stillschweigend Optimalität zu suggerieren.
|
||||
|
||||
**Abhängigkeiten ziehen, was sie brauchen — auch Optionales und nicht
|
||||
Gewähltes.** `+` heißt „für das Ganze entbehrlich“, aber wer per `:#…` darauf
|
||||
zeigt, braucht es eben doch; und ein Ziel in einer nicht gewählten
|
||||
Alternative wird trotzdem realisiert, wenn etwas Nötiges davon abhängt. Im
|
||||
Diagramm bleibt so ein einzelner heller Knoten im zurückgetretenen Zweig
|
||||
stehen — sichtbar „das hier wird gebraucht, egal wie ihr wählt“. Nur
|
||||
**verworfene** Ziele werden nie gezogen (§9: verworfen zählt nie): Sie werden
|
||||
nicht realisiert, und dass der Abhängige deshalb nie fertig wird, sagt schon
|
||||
der effektive Status (D39) — der Pfad muss die Lüge nicht einpreisen.
|
||||
Gezogen wird das Ziel samt **Abwärts**-Realisierung, nicht seine Vorfahren:
|
||||
Der Elternknoten braucht sein Kind, nicht umgekehrt.
|
||||
|
||||
**API: `computeCheapPlan(roots)` → `{set, exact}`.** `computeCheapSet` bleibt
|
||||
als Hülle für Tests und Kompatibilität; `render()` liest `exact` für die
|
||||
Warnung. `markCheapest` entfällt — die Menge entsteht jetzt in einem
|
||||
Worklist-Durchlauf je Belegung (Zyklen enden über die Mengen-Prüfung von
|
||||
selbst; erste ID-Vergabe gewinnt, D36/D39).
|
||||
|
||||
+21
-5
@@ -423,6 +423,24 @@ werden die für die günstigste Realisierung **nötigen** Knoten:
|
||||
- **Fehlende Größe wird als `M` gewertet** (nur für diese Kostenschätzung; die
|
||||
SPEC-Semantik der Größen in §5 bleibt unberührt).
|
||||
|
||||
**Mit Abhängigkeiten (§1) zählt die Dependency Closure.** Jeder nötige Knoten
|
||||
zieht seine `:#…`-Ziele samt deren Realisierung in die nötige Menge; gemeinsam
|
||||
Gebrauchtes zählt über die Mengen-Vereinigung nur **einmal**. Regeln:
|
||||
|
||||
- Abhängigkeiten ziehen ihr Ziel auch dann, wenn es **optional** ist oder in
|
||||
einer **nicht gewählten Alternative** steht — gebraucht ist gebraucht; im
|
||||
Diagramm bleibt so ein einzelner heller Knoten in einem zurückgetretenen
|
||||
Zweig stehen. Nur **verworfene** Ziele werden nie gezogen (verworfen zählt
|
||||
nie); dass so ein Knoten nicht fertig werden kann, zeigt der effektive
|
||||
Status (§4).
|
||||
- Damit ist die Wahl je Alternativgruppe **nicht mehr lokal**: Eine teurere
|
||||
Alternative kann gewinnen, weil ihre Abhängigkeiten anderswo ohnehin
|
||||
bezahlt werden. **Verfahren (D42): erschöpfende Suche** über die
|
||||
**gekoppelten** Gruppen (Gruppen, deren Teilbäume Abhängigkeiten enthalten
|
||||
oder gebraucht werden); alle übrigen wählen lokal wie bisher, bei
|
||||
Gleichstand die erste. Wird die Suche zu groß, rechnet die Anzeige **gierig
|
||||
und sagt es** — Warnung `cheapApprox`, zeilenlos.
|
||||
|
||||
Darstellung per **Inversion**: nicht benötigte Knoten (nicht-gewählte
|
||||
any-of-Alternativen und optionale Knoten, je samt Teilbaum) treten zurück
|
||||
(blass, entsättigt); der
|
||||
@@ -758,11 +776,9 @@ Knoten, Hervorhebung am ausgewählten Knoten. Begründung: D41.
|
||||
|
||||
### Günstigster Pfad mit Abhängigkeiten
|
||||
|
||||
Die Kostenrechnung aus §9 zählt heute nur den gewählten Teilbaum. Mit
|
||||
Abhängigkeiten zählt die **Dependency Closure**: alles, was zusätzlich nötig
|
||||
ist, damit der gewählte Knoten effektiv fertig werden kann. Gemeinsam
|
||||
benötigte Abhängigkeiten werden dabei **nur einmal** gezählt. Genau das macht
|
||||
die Rechnung schwerer als heute — siehe D34.
|
||||
**Umgesetzt** — siehe §9 (Dependency Closure): Vereinigung statt Teilbaum,
|
||||
gemeinsam Gebrauchtes zählt einmal, erschöpfende Suche über die gekoppelten
|
||||
Gruppen mit benanntem gierigem Rückfall. Begründung: D42.
|
||||
|
||||
### Knotenbeschreibungen (`"` und `---`)
|
||||
|
||||
|
||||
+6
-1
@@ -90,10 +90,15 @@ entscheiden, **bevor** Code entsteht.
|
||||
Minimum); **Knotenfarbe = effektiver Status**, bei Diskrepanz die eigene
|
||||
Statusbox als Marke unten links in den eigenen Farben (auch im Export);
|
||||
Tooltip + `a11yEffective`; `tests/effective.test.js`.
|
||||
- [ ] Günstigsten Pfad auf die Dependency Closure umstellen (gemeinsame
|
||||
- [x] Günstigsten Pfad auf die Dependency Closure umstellen (gemeinsame
|
||||
Abhängigkeiten nur einmal zählen). Erweitert D18; die gierige Wahl je
|
||||
Alternativgruppe ist damit nicht mehr optimal — Verfahren wählen und
|
||||
**benennen**.
|
||||
→ Umgesetzt (D42): erschöpfende Suche über die gekoppelten Gruppen
|
||||
(`computeCheapPlan` in model.js), ungekoppelte wählen lokal wie bisher;
|
||||
oberhalb des Suchlimits gierig mit zeilenloser Warnung `cheapApprox`.
|
||||
Abhängigkeiten ziehen auch optionale/nicht gewählte Ziele, verworfene
|
||||
nie; `tests/closure.test.js`.
|
||||
- [x] Querverbindungen zeichnen (eigene SVG-Ebene, optisch sekundär);
|
||||
bei ausgewähltem Knoten ein-/ausgehende hervorheben.
|
||||
→ Umgesetzt (D41): gekrümmte blassgraue Kanten mit Pfeilspitze auf das
|
||||
|
||||
Reference in New Issue
Block a user