📱 Erkannter Endgerättyp ⛱️ Tag und Nacht. Verbraucht keinen oder einen 🍪. 🖼️ Hintergrund ändern. Verbraucht keinen oder einen 🍪.
🧬 0 Ihre DNS in den Krei.se-DNS-Servern, führt zum Bio-Labor 🍪 0 Anzahl Ihrer gespeicherten Kekse, führt zur Keksdose       

🎄 Stabil rekursive Graphen mit KreiseGraph 🍭

Sie kennen es: Sie wollen einen Graphen als Baum ausgeben, doch die nun rekursive Modelleisenbahn auf dem Dachboden lässt das Universum in sich zusammenfallen, weil es die lokalen Zugverbindungen nicht mehr nur einseitig abbildet.

Das muss nicht sein. Die KreiseGraph-Klasse erlaubt fraktale Bäume aus ungerichteten oder gerichteten zyklischen Graphen, ohne externe Exit-Conditions und ohne abzustürzen.

Ich veröffentliche den Code mit Erklärung vorallem, weil ich im beruflichen und privat/philosophischen Kontext sehr häufig mit zyklischen Graphen arbeite oder über davon abgeleitete Konzepte rede. Das erzeugt in manchen Kreisen einen Kurzschluss oder Sorge um die eigene Position im Baum. Aber ich möchte ja keinen ärgern, sondern darlegen, dass diese Systeme sauber designed u.a. sehr schöne Fraktale liefern können.

Beispiel Shareholder als User

Angenommen Sie haben eine Entität für Personen und rechtliche Personen die auch Gruppen sein können. Abgebildet wird beides oft unterschiedlich, aber ob nun eine Investorengruppe eine Firma besitzt oder eine Einzelperson ist an sich egal, wir haben dort bereits Hilfskonstrukte die Gruppen wie eine einzelne Person behandeln.

Problematisch ist weniger die Singular/Plural Frage von Gruppen und Individuen, sondern wie diese Hierarchie als Baum immernoch funktionieren soll wenn er zyklisch wird.

Die Firmeneigner machen nämlich aus dem Startup ein florierendes Unternehmen mit Rechteverwaltung für versch. Gruppen, in denen Shareholder wiederum Teilnehmer sind.

Eigner --> Domäne(Firma) --> Zugriffsrechte --> Gruppen --> Teilnehmer

Da in den meisten Unternehmen rechtliches, Buchhaltung und IT getrennt ist macht das i.d.R. keine Probleme, die bekommen Sie erst, wenn Sie all diese Bereiche aus einem einzigen Graphen ausleiten wollen.

Denn hier wäre ein Shareholder sowohl Besitzer der Domäne, als auch Teil der Gruppe Management um ein geteiltes Postfach zu nutzen oder Zugriffsrechte abzufragen. Der Baum hat an dieser Stelle nun einen rekursiven Verweis, doch wo setzt man nun die Grenze für eine Abfrage? PHP gibt ab einem Knoten der schonmal aufgerufen wurde nur "REKURSION" aus und beendet die Schleife. Neo4js (Graphendatenbank immerhin) hat ähnliche wie Symlinks in Linux-Dateisystemen eine maximale Pfadlänge. Die bringt uns hier aber auch nicht weiter, denn ob wir nun nach 3 oder 10 Einträgen eine Rekursion haben oder eine Darstellung brauchen die 100 oder 10000 Knotenpunkte enthält - das wissen Sie in der Regel vorher nicht. Definieren wir es vorher fest begrenzen wir die Aussagekraft des Graphen (und handeln uns tolle Bugs in Zukunft ein die man nahezu unmöglich wiederfindet).

Bekannter Lösungsansatz: Besuchte Knoten (z.B. PHP)

Gelöst wird ein Ablaufen des Graphen um Bäume zu generieren durch Markierung besuchter Knoten. Problem: Wenn Sie jetzt von einer Person wissen wollen welche Domänen Sie besitzt finden Sie in diesem Baum keinen Hinweis mehr darauf, dass diese Person dort im Management sitzt. Spätestens wenn Sie abbilden möchten, dass nur bestimmte Mitarbeiter Firmenanteile zeichnen können wird es also schwierig.

Bekannter Lösungsansatz: Maximale Pfadlänge (z.B. Linux Symlinks)

Hier wird der ausgegebene Pfad begrenzt. Ob eine Rekursion vorliegt oder nicht ist völlig egal, sobald es von der Wurzel bis zum Blatt-Knoten eine bestimmte Länge erreicht hat werden keine weiteren Knoten abgelaufen. Problem ist hier die Hartkodierung des Limits, denn es gibt z.B. Ordnungsfanatiker die gern längere Pfade hätten und die unter Linux nicht bekommen. Anderen wäre fast lieber das Dateisystem wüsste, dass ich beim Indizieren von Videos nicht 100x die gleichen Querverweise zu Darstellern und Genre ablaufen will. Schon eine einfache Filmdatenbank nur mit Links zu den Darstellern und dem Genre erstellt Ihnen zwangsläufig eine Bibliothek die kein Medienserver zuverlässig indiziert.

Lösbar wäre hier nur für den Symlink-Typ der alle Genres beschreibt (also für alle Symlinks in einem Ordner) die Länge aller danach enstehenden Pfade auf 1 oder 2 weitere Verweise zu begrenzen. Das wäre einfach zu implementieren - geht aber nicht.

Mein erster Lösungsansatz: Ermüdende Knoten

Mein erster Lösungsansatz bestand darin für jeden Knoten eine Maximal-Lebenszeit festzulegen. Diese funktioniert ganz einfach: Der Knoten hat standardmässig 10 Besuche, danach gibt er keine Kanten / benachbarten Knoten mehr aus und die Abbildung als Baum / Fraktal endet. Problem gelöst. Rufe ich nun den Graphen z.B. ab Punkt Zugriffrechte aus, ergibt sich:

Zugriffsrechte --> Gruppen --> Teilnehmer --> Domänen --> Zugriffrechte --> Gruppen, etc. pp ... bis Zugriffsrechte 10x besucht wurde und der Baum endet.

Aktueller Lösungsansatz: Einmal aussetzen bitte

Mein derzeitiger Lösungsansatz ist noch etwas eleganter, da er den Bockenden zum Gärtner macht. Da ein Knoten in der Kategorientheorie auch eine Funktion sein kann die mehr kann als Schubladen zu öffnen macht es Sinn keine Standard-Werte für Ermüdung festzulegen - denn manche Funktionen werden sehr häufig aufgerufen, andere nicht. Manchmal liefert ein Knoten nur Infos darüber welche Elemente in seiner Schublade liegen. Manchmal wird er längere Berechnungen anstrengen oder je nachdem von WO auf diesen Knoten zugegriffen wird andere Ergebnisse liefern.

Nun für jeden Knoten einzeln festzulegen, dass er 1000x aufgerufen werden darf, andere 10x macht auf lange Sicht in Wissens- oder Rechengraphen wenig Sinn, denn Sie müssten wieder zu jedem Zeitpunkt des Systemdesigns wissen welcher Knoten wie lange leben darf - und das Problem immer alle Aspekte des Graphen kennen zu müssen soll der Ansatz ja lösen. Es sollte egal sein an welchem Punkt / mit welcher Perspektive Sie auf den Graphen schauen und für den aktuellen Punkt nur wichtig sein welche Knoten als Nachbarn auftreten und nicht welche Eigenheiten diese noch haben. Denn erst wenn nur die direkten Nachbarknoten zu beachten sind können Sie sich maximal mit Ihrem Arbeitsgedächtnis auf 1 Aspekt konzentrieren.

Daher sind prinzipiell alle Knoten "unsterblich", aber haben eine Bedingung bei einer Anfrage auch mal einmalig keine Kanten auszugeben. Das kann ein Zeitlimit sein oder ein Ressourcenlimit, oder eine feste Anzahl an Wiederholungen, gemein ist allen internen Bedingungen das Aussetzen, also dass Sie dies nur 1x tun und dann die Bedingung fürs Aussetzen resetten. Ich habe mich als Standard auf 12 Wiederholungen festgelegt, d.h. alle Knoten können 12x aufgerufen werden, liefern dann 1x keine Kanten und setzen sich wieder auf 12 zurück. Es kann auch ein Ressourcenlimit auf Zeit sein - wenn Sie wissen wieviel RAM und Zeit ihr Graph maximal zur Verfügung haben soll können Sie diesen Wert auf alle Knoten aufteilen.

Der Code für 3 Generationen sieht dann z.B. so aus:

$root = new KreiseGraph('Root');

$first  = new KreiseGraph('First');
$second = new KreiseGraph('Second');
$third  = new KreiseGraph('Third');
$fourth = new KreiseGraph('Fourth');
$fifth  = new KreiseGraph('Fifth');

$firstChild      = new KreiseGraph('firstChild');
$secondChild     = new KreiseGraph('secondChild');
$thirdChild      = new KreiseGraph('thirdChild');
$fourthChild     = new KreiseGraph('fourthChild');

$firstGrandChild      = new KreiseGraph('firstGrandChild');
$secondGrandChild     = new KreiseGraph('secondGrandChild');
$thirdGrandChild      = new KreiseGraph('thirdGrandChild');
$fourthGrandChild     = new KreiseGraph('fourthGrandChild');

// construct the graph from the bottom up to not trigger
// raising counters on parents too much

$firstChild->firstGrandChild = clone $firstGrandChild;
$firstChild->secondGrandChild = clone $secondGrandChild;

$secondChild->firstGrandChild = clone $firstGrandChild;
$secondChild->secondGrandChild = clone $secondGrandChild;

$first->firstChild = clone $firstChild;
$first->secondChild = clone $secondChild;
$first->thirdChild = clone $thirdChild;

$second->firstChild = clone $firstChild;
$second->secondChild = clone $secondChild;
$second->thirdChild = clone $thirdChild;

$third->firstChild = clone $firstChild;
$third->secondChild = clone $secondChild;
$third->thirdChild = clone $thirdChild;

$root->First = clone $first;
$root->Second = clone $second;
$root->Third = clone $third;

// loops

$root->First->firstChild->secondGrandChild->secondChild = $root->Second->secondChild;
$root->Second->secondChild->First = $root->First;
$root->Third->secondChild->Second = $root->Second;

// you should now reset all counters after constructing the graph
// as PHP will raise counters during construction

Die Ausgabe dieses nachträglich als rekursiven Graphen umgebogenen Abstammungsbaum finden Sie hier:

Root (6/12)
__ First (1/12)
____ firstChild (1/12)
______ firstGrandChild (0/12)
______ secondGrandChild (0/12)
________ secondChild (0/12)
__________ firstGrandChild (0/12)
__________ secondGrandChild (0/12)
__________ First (2/12)
____________ firstChild (2/12)
______________ firstGrandChild (1/12)
______________ secondGrandChild (1/12)
________________ secondChild (1/12)
__________________ firstGrandChild (1/12)
__________________ secondGrandChild (1/12)
__________________ First (3/12)
____________________ firstChild (3/12)
______________________ firstGrandChild (2/12)
______________________ secondGrandChild (2/12)
________________________ secondChild (2/12)
__________________________ firstGrandChild (2/12)
__________________________ secondGrandChild (2/12)
__________________________ First (4/12)
____________________________ firstChild (4/12)
______________________________ firstGrandChild (3/12)
______________________________ secondGrandChild (3/12)
________________________________ secondChild (3/12)
__________________________________ firstGrandChild (3/12)
__________________________________ secondGrandChild (3/12)
__________________________________ First (5/12)
____________________________________ firstChild (5/12)
______________________________________ firstGrandChild (4/12)
______________________________________ secondGrandChild (4/12)
________________________________________ secondChild (4/12)
__________________________________________ firstGrandChild (4/12)
__________________________________________ secondGrandChild (4/12)
__________________________________________ First (6/12)
____________________________________________ firstChild (6/12)
______________________________________________ firstGrandChild (5/12)
______________________________________________ secondGrandChild (5/12)
________________________________________________ secondChild (5/12)
__________________________________________________ firstGrandChild (5/12)
__________________________________________________ secondGrandChild (5/12)
__________________________________________________ First (7/12)
____________________________________________________ firstChild (7/12)
______________________________________________________ firstGrandChild (6/12)
______________________________________________________ secondGrandChild (6/12)
________________________________________________________ secondChild (6/12)
__________________________________________________________ firstGrandChild (6/12)
__________________________________________________________ secondGrandChild (6/12)
__________________________________________________________ First (8/12)
____________________________________________________________ firstChild (8/12)
______________________________________________________________ firstGrandChild (7/12)
______________________________________________________________ secondGrandChild (7/12)
________________________________________________________________ secondChild (7/12)
__________________________________________________________________ firstGrandChild (7/12)
__________________________________________________________________ secondGrandChild (7/12)
__________________________________________________________________ First (9/12)
____________________________________________________________________ firstChild (9/12)
______________________________________________________________________ firstGrandChild (8/12)
______________________________________________________________________ secondGrandChild (8/12)
________________________________________________________________________ secondChild (8/12)
__________________________________________________________________________ firstGrandChild (8/12)
__________________________________________________________________________ secondGrandChild (8/12)
__________________________________________________________________________ First (10/12)
____________________________________________________________________________ firstChild (10/12)
______________________________________________________________________________ firstGrandChild (9/12)
______________________________________________________________________________ secondGrandChild (9/12)
________________________________________________________________________________ secondChild (9/12)
__________________________________________________________________________________ firstGrandChild (9/12)
__________________________________________________________________________________ secondGrandChild (9/12)
__________________________________________________________________________________ First (11/12)
____________________________________________________________________________________ firstChild (11/12)
______________________________________________________________________________________ firstGrandChild (10/12)
______________________________________________________________________________________ secondGrandChild (10/12)
________________________________________________________________________________________ secondChild (10/12)
__________________________________________________________________________________________ firstGrandChild (10/12)
__________________________________________________________________________________________ secondGrandChild (10/12)
__________________________________________________________________________________________ First (12/12)
____________________________________________________________________________________ secondChild (0/12)
______________________________________________________________________________________ firstGrandChild (11/12)
______________________________________________________________________________________ secondGrandChild (11/12)
____________________________________________________________________________________ thirdChild (0/12)
____________________________________________________________________________ secondChild (1/12)
______________________________________________________________________________ firstGrandChild (12/12)
______________________________________________________________________________ secondGrandChild (12/12)
____________________________________________________________________________ thirdChild (1/12)
____________________________________________________________________ secondChild (2/12)
______________________________________________________________________ firstGrandChild (0/12)
______________________________________________________________________ secondGrandChild (0/12)
____________________________________________________________________ thirdChild (2/12)
____________________________________________________________ secondChild (3/12)
______________________________________________________________ firstGrandChild (1/12)
______________________________________________________________ secondGrandChild (1/12)
____________________________________________________________ thirdChild (3/12)
____________________________________________________ secondChild (4/12)
______________________________________________________ firstGrandChild (2/12)
______________________________________________________ secondGrandChild (2/12)
____________________________________________________ thirdChild (4/12)
____________________________________________ secondChild (5/12)
______________________________________________ firstGrandChild (3/12)
______________________________________________ secondGrandChild (3/12)
____________________________________________ thirdChild (5/12)
____________________________________ secondChild (6/12)
______________________________________ firstGrandChild (4/12)
______________________________________ secondGrandChild (4/12)
____________________________________ thirdChild (6/12)
____________________________ secondChild (7/12)
______________________________ firstGrandChild (5/12)
______________________________ secondGrandChild (5/12)
____________________________ thirdChild (7/12)
____________________ secondChild (8/12)
______________________ firstGrandChild (6/12)
______________________ secondGrandChild (6/12)
____________________ thirdChild (8/12)
____________ secondChild (9/12)
______________ firstGrandChild (7/12)
______________ secondGrandChild (7/12)
____________ thirdChild (9/12)
____ secondChild (10/12)
______ firstGrandChild (8/12)
______ secondGrandChild (8/12)
____ thirdChild (10/12)
__ Second (2/12)
____ firstChild (0/12)
______ firstGrandChild (11/12)
______ secondGrandChild (11/12)
________ secondChild (11/12)
__________ firstGrandChild (9/12)
__________ secondGrandChild (9/12)
__________ First (0/12)
____________ firstChild (12/12)
____________ secondChild (11/12)
______________ firstGrandChild (10/12)
______________ secondGrandChild (10/12)
____________ thirdChild (11/12)
____ secondChild (12/12)
____ thirdChild (0/12)
__ Third (1/12)
____ firstChild (0/12)
______ firstGrandChild (12/12)
______ secondGrandChild (12/12)
____ secondChild (0/12)
______ firstGrandChild (11/12)
______ secondGrandChild (11/12)
______ Second (3/12)
________ firstChild (1/12)
__________ firstGrandChild (0/12)
__________ secondGrandChild (0/12)
____________ secondChild (0/12)
______________ firstGrandChild (12/12)
______________ secondGrandChild (12/12)
______________ First (1/12)
________________ firstChild (0/12)
__________________ firstGrandChild (1/12)
__________________ secondGrandChild (1/12)
____________________ secondChild (1/12)
______________________ firstGrandChild (0/12)
______________________ secondGrandChild (0/12)
______________________ First (2/12)
________________________ firstChild (1/12)
__________________________ firstGrandChild (2/12)
__________________________ secondGrandChild (2/12)
____________________________ secondChild (2/12)
______________________________ firstGrandChild (1/12)
______________________________ secondGrandChild (1/12)
______________________________ First (3/12)
________________________________ firstChild (2/12)
__________________________________ firstGrandChild (3/12)
__________________________________ secondGrandChild (3/12)
____________________________________ secondChild (3/12)
______________________________________ firstGrandChild (2/12)
______________________________________ secondGrandChild (2/12)
______________________________________ First (4/12)
________________________________________ firstChild (3/12)
__________________________________________ firstGrandChild (4/12)
__________________________________________ secondGrandChild (4/12)
____________________________________________ secondChild (4/12)
______________________________________________ firstGrandChild (3/12)
______________________________________________ secondGrandChild (3/12)
______________________________________________ First (5/12)
________________________________________________ firstChild (4/12)
__________________________________________________ firstGrandChild (5/12)
__________________________________________________ secondGrandChild (5/12)
____________________________________________________ secondChild (5/12)
______________________________________________________ firstGrandChild (4/12)
______________________________________________________ secondGrandChild (4/12)
______________________________________________________ First (6/12)
________________________________________________________ firstChild (5/12)
__________________________________________________________ firstGrandChild (6/12)
__________________________________________________________ secondGrandChild (6/12)
____________________________________________________________ secondChild (6/12)
______________________________________________________________ firstGrandChild (5/12)
______________________________________________________________ secondGrandChild (5/12)
______________________________________________________________ First (7/12)
________________________________________________________________ firstChild (6/12)
__________________________________________________________________ firstGrandChild (7/12)
__________________________________________________________________ secondGrandChild (7/12)
____________________________________________________________________ secondChild (7/12)
______________________________________________________________________ firstGrandChild (6/12)
______________________________________________________________________ secondGrandChild (6/12)
______________________________________________________________________ First (8/12)
________________________________________________________________________ firstChild (7/12)
__________________________________________________________________________ firstGrandChild (8/12)
__________________________________________________________________________ secondGrandChild (8/12)
____________________________________________________________________________ secondChild (8/12)
______________________________________________________________________________ firstGrandChild (7/12)
______________________________________________________________________________ secondGrandChild (7/12)
______________________________________________________________________________ First (9/12)
________________________________________________________________________________ firstChild (8/12)
__________________________________________________________________________________ firstGrandChild (9/12)
__________________________________________________________________________________ secondGrandChild (9/12)
____________________________________________________________________________________ secondChild (9/12)
______________________________________________________________________________________ firstGrandChild (8/12)
______________________________________________________________________________________ secondGrandChild (8/12)
______________________________________________________________________________________ First (10/12)
________________________________________________________________________________________ firstChild (9/12)
__________________________________________________________________________________________ firstGrandChild (10/12)
__________________________________________________________________________________________ secondGrandChild (10/12)
____________________________________________________________________________________________ secondChild (10/12)
______________________________________________________________________________________________ firstGrandChild (9/12)
______________________________________________________________________________________________ secondGrandChild (9/12)
______________________________________________________________________________________________ First (11/12)
________________________________________________________________________________________________ firstChild (10/12)
__________________________________________________________________________________________________ firstGrandChild (11/12)
__________________________________________________________________________________________________ secondGrandChild (11/12)
____________________________________________________________________________________________________ secondChild (11/12)
______________________________________________________________________________________________________ firstGrandChild (10/12)
______________________________________________________________________________________________________ secondGrandChild (10/12)
______________________________________________________________________________________________________ First (12/12)
________________________________________________________________________________________________ secondChild (12/12)
________________________________________________________________________________________________ thirdChild (12/12)
________________________________________________________________________________________ secondChild (0/12)
__________________________________________________________________________________________ firstGrandChild (11/12)
__________________________________________________________________________________________ secondGrandChild (11/12)
________________________________________________________________________________________ thirdChild (0/12)
________________________________________________________________________________ secondChild (1/12)
__________________________________________________________________________________ firstGrandChild (12/12)
__________________________________________________________________________________ secondGrandChild (12/12)
________________________________________________________________________________ thirdChild (1/12)
________________________________________________________________________ secondChild (2/12)
__________________________________________________________________________ firstGrandChild (0/12)
__________________________________________________________________________ secondGrandChild (0/12)
________________________________________________________________________ thirdChild (2/12)
________________________________________________________________ secondChild (3/12)
__________________________________________________________________ firstGrandChild (1/12)
__________________________________________________________________ secondGrandChild (1/12)
________________________________________________________________ thirdChild (3/12)
________________________________________________________ secondChild (4/12)
__________________________________________________________ firstGrandChild (2/12)
__________________________________________________________ secondGrandChild (2/12)
________________________________________________________ thirdChild (4/12)
________________________________________________ secondChild (5/12)
__________________________________________________ firstGrandChild (3/12)
__________________________________________________ secondGrandChild (3/12)
________________________________________________ thirdChild (5/12)
________________________________________ secondChild (6/12)
__________________________________________ firstGrandChild (4/12)
__________________________________________ secondGrandChild (4/12)
________________________________________ thirdChild (6/12)
________________________________ secondChild (7/12)
__________________________________ firstGrandChild (5/12)
__________________________________ secondGrandChild (5/12)
________________________________ thirdChild (7/12)
________________________ secondChild (8/12)
__________________________ firstGrandChild (6/12)
__________________________ secondGrandChild (6/12)
________________________ thirdChild (8/12)
________________ secondChild (9/12)
__________________ firstGrandChild (7/12)
__________________ secondGrandChild (7/12)
________________ thirdChild (9/12)
________ secondChild (12/12)
________ thirdChild (1/12)
____ thirdChild (0/12)

Sie sehen es wird mit 3 Generationen schon sehr komplex, aber der Graph liefert immernoch Ergebnisse ohne abzustürzen - es ist keine externe Exit-Condition wie bei einer Fraktalgenerierung notwendig (bei Mandelbrot brechen Sie die Berechnung ab wenn das Ergebnis sehr groß ist) und das Prinzip funktioniert auch noch, wenn Sie pro Knoten unterschiedliche Algorithmen laufen lassen.

Der Baum endet früher oder später immer - einmal aussetzen genügt. Sie denken jetzt: Ja aber es gibt bestimmt Edge-Cases in denen die Knoten sich so resetten, dass sie immer wieder aufgerufen werden. Aber das stimmt nicht. Denn Sie leiten den Baum immer von einem Eingangs-Knoten im Graphen ab. Und spätestens dieser setzt auch irgendwann 1x aus. Der Fall, dass der Graph sich so stabil hält dass er unendlich ableitet kann nur eintreten wenn Sie dieselbe Instanz aus 2 Eingangspunkten ansteuern (was Sie nicht tun sollten, aber vielleicht finde ich auch für den geteilten Besuchs-Speicher noch eine Lösung).

Wofür brauch ich das?

Ich weiß nicht wie es Ihnen geht, meine Millersche Zahl ist selbst bei wochenlang brachliegendem Sozialkontakt mit 10 bis 12 am Limit - für die FullStack-Router brauchen Sie z.B. etwa 15-20 stark miteinander agierende Knoten und das Konstrukt ist damit bereits minimal wackelig. Bedeutet: Ich weiß selbst nicht mehr so genau wie es funktioniert und muss mich nicht nur auf einen Teilaspekt meiner Software erneut einstellen (was noch ok wäre) sondern auch in diesem Teilaspekt bereits Anpassungen auf mehrere Konzentrationen aufteilen.

Ein bisschen Abwechslung und Spaß zwischenduch in 9 Dimensionen finden Sie hier: https://www.youtube.com/watch?v=Kdsj84UdeYg

Stemmen werden Sie diese Komplexität als Einzelperson nur, wenn Sie Gedankenschleifen als solche erkennen und wissend um Ihre eigene Beschränktheit willens sind nicht erst eine Pause einzulegen wenn die externe Exit-Condition greift (Sie drehen durch oder haben einfach keine Energie mehr), sondern die zu häufige Wiederholung eines Teilaspekts Sie darauf hinweist den Knoten aufzuteilen und die Architektur an dieser Stelle aufzuspalten.

Wie diese Aufspaltung aussehen kann merken Sie auch privat, wenn Sie an Routinen festhalten, aber ausprobieren was ein einmaliges anderes Verhalten für eine Wirkung zeigt.

Das geht aber nicht wenn ihre Architektur verlangt, dass Sie nicht an einem Teil des Graphen arbeiten können ohne alle anderen mit zu beeinflussen. Sie können z.B. oft keine Legacy-Architektur refaktorisieren weil diese dem festen Gedankenmodell eines einzigen Menschen entspringt. Diese Sichtweise können Sie beibehalten, da seine Perspektive immernoch 1 Knoten sieht, Sie aber dessen Funktion in mehrere Knoten aufteilen. Die Schnittstelle für Altcode bleibt so erhalten und für neuen Code rufen Sie die jeweiligen neuen Knoten auf.

Sie können mit Knoten ohne festgelegte Limits also Wissensgraphen bauen die Ihr Arbeitsgedächtnis gerade noch stemmen kann und Sie können nun überall im Rechen-Graphen ansetzen und eine Perspektive einnehmen die immernoch Ergebnisse liefert.

Im Idealfall erhalten Sie so perspektiv-agnostisch erklärbare Rechenmodelle - jeder Teilnehmer wird den Graphen aus einer anderen Perspektive sehen. Mit KI geht das nicht, denn ausser festem Input und Output sind alle Knoten "versteckt" und nicht von einem Menschen aufgebaut, daher auch nicht erklärbar wozu Sie eigentlich da sind. Wikidata ist da, finde ich, mit seiner Triple-Architektur Subjekt - Prädikat - Objekt besser.

Das liegt an der festen Herangehensweise einen Zugangspunkt und ein Ergebnis im Graphen zu setzen, denn sonst können Sie die Funktion nicht ableiten, aber den gibt es hier prinzipiell nicht: WO Sie den Graphen als Baum ausleiten und wie weit ist egal: Der Punkt des Firmeneigners wird aus jeder Perspektive rekursiv auftauchen - ohne den Graphen abstürzen zu lassen. Optimieren können Sie diese Systeme zwar nur mit Menschen oder Agenten die menschliche Beschränkungen haben, aber hey, so werden die Coder wenigstens nicht alle arbeitslos.

Beispielcode für TypeScript

Eine Implementierung für TypeScript, in PHP sind mehr Klimmzüge für die Proxies nötig. Wie man unschwer erkennt nutze ich den KreiseGraphen hier um ThreeJS-Modelle als Körper auszugeben.

export default class KreiseGraph {

        // Structural
        public name: string | undefined = undefined;
        private graphs: GraphsInterface = {}

        public repeat: number = 12                      // how often do  we visit this node in a circular reference?
        public visited: number = 0                      // how often did we visit this node in a circular reference?

        // ThreeJS. These will still trigger the get and set traps, but as 
        // we do (return) target[property] (=) this uses the correct interfaces
        public objects: ObjectsInterface = {}
        public groups: GroupRecordType = {}
        public kreiseMeshes: KreiseMeshesRecordType = {}
        public meshes: MeshesRecordType = {}

        public lights: LightsRecordType = {}

        public helpers: HelpersRecordType = {}

        // computable node stuff

        // mostly unused, useful for eva(input) and moving the goalpost in the base (start and ending) graph
        public input: any
        public targetOutput: any

        [key: string]: any // when we add properties and functions into .
        [key: symbol]: any

        constructor(name?: string) {

                this.name = name;

                // this makes the whole class instance a proxy, so this will always fire even if .graphs, etc. is public
                return new Proxy(this, {
                        get(target: KreiseGraph, property) { // (, receiver)
                                // catches properties, methods and class instances
                                if (property in target) {

                                        // raise counter on subgraphs-access
                                        // Note that this will raise the counter if you use instance.graphs.newGraph = ...
                                        if (property === 'graphs') {
                                                target.visited++
                                                // catch tired nodes and return nothing
                                                // but reset the counter
                                                if (target.visited > target.repeat) {
                                                        target.visited = 0;
                                                        return;
                                                }
                                        }
                                        return target[property]
                                }
                                // target not found
                                return undefined
                        },
                        // you can type property as string to make sure we overwrite the value
                        // but if you do instance.propertyName = xyz it will always be typed as string
                        set(target: KreiseGraph, property, value) { // (, receiver)

                                // catch KreiseGraphs as graph to not raise counter on doing instance.graphs.newGraph =
                                if (value instanceof KreiseGraph) {
                                        // If it is, store it in the 'graphs' property and set the name,
                                        // as we cannot set the name one-lined in the constructor

                                        // hey future me: target is this KreiseGraph-instance itself
                                        // so .graphs will NOT raise the counter 
                                        target.graphs[property] = value;
                                        // set the KreiseGraphs.name only if its empty
                                        if (!target.graphs[property].name)
                                                target.graphs[property].name = property as string;
                                } else {
                                        // Otherwise, treat it as a regular property
                                        target[property] = value;
                                }
                                return true
                        },
                        getPrototypeOf(target: KreiseGraph) {
                                return Object.getPrototypeOf(target)
                        },
                        has(target: KreiseGraph, property) {
                                return property in target || property in target.objects
                        }
                })

        }

        eva(input: any): any {

                /*

                // example method to sum graph outputs
                let output: any = input
                Object.entries(this.graphs).forEach(([graphName, graph]) => {

                output += graph.eva(null)

                })
                return output

                // example to add up to 100 new neurons
                for (i = this.graphs.count(); i <= Math.random() * 100 + this.graphs.count(); i++) {

                this.graphs[i] = new KreiseGraph
                this.graphs[i].eva = this.eva() // copy eva method

                }

                */

                return input

        }

}

Beispielcode PHP

PHP ist deutlich anstrengender was magic methods angeht. Nun bauen wir hier zwar Klassen die sich selbst als Klassen ausgibt, aber ich gebe zu, dass TypeScript hier eleganter war:

class KreiseGraph
{

        /**
        * The name of the graph.
        *
        * @var string|null
        */
        public ?string $name = null;

        /**
        * The sub-graphs of this graph.
        *
        * @var KreiseGraph[]
        */
        private array $graphs = [];

        private array $data = [];

        public int $repeat = 12;
        public int $visited = 0;

        public function __construct(?string $name = null)
        {

                $this->name = $name;
                if ($this->visited > $this->repeat) {
                        return [];
                }
                return $this;
        }

        public function getGraphs()
        {
                // this def. raises the counter ;)
                $this->visited++;
                if ($this->visited > $this->repeat) {
                        $this->visited = 0;
                        return [];
                }
                // $shuffled = $this->graphs;
                // shuffle($shuffled);
                // return $shuffled;
                return $this->graphs;
        }

        // in php the class does NOT become a proxy instance
        public function __get($property)
        {
                // catches properties and class instances
                if (array_key_exists($property, $this->data)) {

                        return $this->data[$property];
                }

                // catches graphs called by name

                // raise counter on subgraphs-access.
                // Note that this will raise the counter if you use $instance->graphs->newGraph = ...
                if (array_key_exists($property, $this->graphs)) {

                        //echo "accessing subgraph in " . $this->name . " \n";

                        $this->visited++;
                        // catch tired nodes and return nothing
                        // but reset the counter
                        if ($this->visited > $this->repeat) {
                                $this->visited = 0;
                                return;
                        }
                        return $this->graphs[$property];
                }
                return;
        }

        // hey future me: if you forgot, __call gets triggered on methods, never on class instances themselves
        public function __call($property, $args)
        {
                // catches methods
                if (array_key_exists($property, $this->data)) {
                        if (is_callable($this->data[$property])) {
                                return call_user_func_array($this->data[$property], $args);
                        }
                }
                return;
        }

        public function __set($property, $value)
        {
                if (is_object($value)) {
                        if (get_class($value) == 'KreiseGraph') {
                                $this->graphs[$property] = $value;
                                // set the KreiseGraphs.name only if its empty
                                if (!$this->graphs[$property]->name)
                                        $this->graphs[$property]->name = $property;
                        } else {
                                // property
                                $this->data[$property] = $value;
                        }
                } else {
                        // property
                        $this->data[$property] = $value;
                }
        }

        public function __isset($property)
        {
                return
                        array_key_exists($property, $this->data) ||
                        array_key_exists($property, $this->graphs);
        }

        public function unset($property)
        {
                if (array_key_exists($property, $this->graphs)) {
                        unset($this->graphs[$property]);
                }

                if (array_key_exists($property, $this->data)) {
                        unset($this->data[$property]);
                }
        }
}

Prinzipiell kann PHP aber auch unterscheiden ob eine Klassen-Eigenschaft Daten enthält, eine Funktion ist oder ein benachbarter KreiseGraph.

Es ist schon eine Weile her, aber ich glaube in TypeScript war es auch einfacher den Graphen aufzubauen ohne bereits die Counter zu erhöhen. Alternativ kann man nach dem Verschalten der Knoten natürlich alle auf 0 setzen.

Alles: Eine Frage der Perspektive

Mir hat das Konstrukt auch geholfen besser mit anderen zusammenzuarbeiten und daher veröffentliche ich die Idee auch gern und freue mich zu sehen welche Fraktale andere damit vielleicht bauen können!

Denn letzten Endes ist dies nur meine persönliche Sicht auf die Dinge - und Ihre darf gern eine andere sein!

Kleiner Link zu Game Theory

Quellen gibt es hier keine, aber natürlich habe ich ähnliche Ideen auch schon woanders erlebt: Die Evolution des Vertrauens

Ich denke der Ansatz generell bis zum Ressourcenlimit zu antworten ist eleganter, aber hier geht es natürlich um die recht gleichverteilten Ressourcen einzelner Menschen (und nicht Maschinen wie in meinem Fall).

Auch würde ich den Ansatz minimal kritisieren weil hier feste Größen gewählt werden die in der Realität nie so fest sind - zudem ist eine Kommunikation der Spielregeln bei Zero-Trust Umgebungen nur die Garantie dafür, dass jemand das Limit bis zum Anschlag dehnen wird. Sie können mit KreiseGraphen ein Soft-Limit implementieren, dass die Stabilität des Graphen IMMER sicherstellt und zudem einem Querier der versucht den Graphen gezielt zu destabilisieren draussenhält ohne ihn völlig zu blocken. Denn ob ein Querier sein Verhalten geändert hat wissen Sie nicht mit einem Hardblock. Aber das ist ein anderes Thema.