diff --git a/Cluster.cs b/Cluster.cs index 3aa1945..61e2897 100644 --- a/Cluster.cs +++ b/Cluster.cs @@ -18,6 +18,7 @@ public class Cluster : Nucleus { this.parent?.nuclei.Add(this); ClonePrefab(); + _ = this.inputs; this.sortedNuclei = TopologicalSort(this.nuclei); // Does not work because we have nuclei with the same names in an nucleusArray // 'Pheromone steering' @@ -33,6 +34,7 @@ public class Cluster : Nucleus { this.cluster.nuclei.Add(this); ClonePrefab(); + _ = this.inputs; this.sortedNuclei = TopologicalSort(this.nuclei); //this.nucleiDict = nuclei.ToDictionary(nucleus => nucleus.name); } @@ -127,6 +129,7 @@ public class Cluster : Nucleus { if (inDegree[node] == 0) // Nodes with no dependencies queue.Enqueue(node); } + // The queue basically stores all input nuclei? List sortedOrder = new(); while (queue.Count > 0) { @@ -200,11 +203,107 @@ public class Cluster : Nucleus { if (nucleus.synapses.Count == 0) this._inputs.Add(nucleus); } + ComputeOrders(); } return this._inputs; } } + public Dictionary> computeOrders = new(); + private void ComputeOrders() { + foreach (Nucleus input in this._inputs) { + computeOrders[input] = TopologicalSort2(input); + } + } + + private List TopologicalSort2(Nucleus startNode) { + Dictionary inDegree = new Dictionary(); + HashSet visited = new HashSet(); + + // Initialize in-degrees and mark all nodes as unvisited + foreach (Nucleus node in this.nuclei) { + inDegree[node] = 0; + } + + // Calculate in-degrees for all nodes reachable from the start node + Queue queue = new Queue(); + queue.Enqueue(startNode); + visited.Add(startNode); + + while (queue.Count > 0) { + Nucleus current = queue.Dequeue(); + foreach (Nucleus receiver in current.receivers) { + if (!visited.Contains(receiver)) { + visited.Add(receiver); + queue.Enqueue(receiver); + } + inDegree[receiver]++; + } + } + + // Perform topological sort on all reachable nodes + queue.Clear(); + foreach (var node in visited) { + if (inDegree[node] == 0) { + queue.Enqueue(node); + } + } + + List sortedOrder = new List(); + while (queue.Count > 0) { + Nucleus current = queue.Dequeue(); + sortedOrder.Add(current); // Process the node + + foreach (Nucleus receiver in current.receivers) { + if (visited.Contains(receiver)) { + inDegree[receiver]--; + if (inDegree[receiver] == 0) // If all dependencies resolved + queue.Enqueue(receiver); + } + } + } + + // Check for cycles in the graph + if (sortedOrder.Count != visited.Count) + throw new InvalidOperationException("Graph is not a DAG; a cycle exists."); + + return sortedOrder; + } + + private List TopologicalSort3(Nucleus startNode) { + Dictionary inDegree = new(); + foreach (Nucleus node in this.nuclei) + inDegree[node] = 0; // Initialize in-degree to zero + + // Calculate in-degrees + foreach (Nucleus node in this.nuclei) { + foreach (Nucleus receiver in node.receivers) + inDegree[receiver]++; + } + + Queue queue = new(); + queue.Enqueue(startNode); + + List sortedOrder = new(); + while (queue.Count > 0) { + Nucleus current = queue.Dequeue(); + sortedOrder.Add(current); // Process the node + + foreach (Nucleus receiver in current.receivers) { + inDegree[receiver]--; + if (inDegree[receiver] == 0) // If all dependencies resolved + queue.Enqueue(receiver); + } + } + + Debug.Log($"Compute order for {startNode.name} length = {sortedOrder.Count}"); + // Check for cycles in the graph + // if (sortedOrder.Count != this.nuclei.Count) + // throw new InvalidOperationException("Graph is not a DAG; a cycle exists."); + + return sortedOrder; + } + public virtual Nucleus output {//=> this.nuclei[0] as Nucleus; get { if (this.nuclei.Count > 0) @@ -250,6 +349,24 @@ public class Cluster : Nucleus { #region Update + public void UpdateFromNucleus(Nucleus startNucleus) { + // no bias+synapse input state calculation for now... + + List computeOrder = this.computeOrders[startNucleus]; + if (startNucleus.trace) + Debug.Log($"Update from {startNucleus.name}"); + foreach (Nucleus nucleus in computeOrder) { + nucleus.UpdateStateIsolated(); + if (startNucleus.trace) + Debug.Log($" {nucleus.name} = {nucleus.outputValue}"); + } + + this.outputValue = this.output.outputValue; + this.stale = 0; + + UpdateNuclei(); + } + public override void UpdateStateIsolated() { Vector3 sum = this.bias; @@ -263,7 +380,7 @@ public class Cluster : Nucleus { foreach (Nucleus nucleus in this.sortedNuclei) nucleus.UpdateStateIsolated(); - + this.outputValue = this.output.outputValue; this.stale = 0; @@ -271,9 +388,9 @@ public class Cluster : Nucleus { } public override void UpdateNuclei() { - this.stale++; - if (this.stale > staleValueForSleep) - _outputValue = Vector3.zero; + // this.stale++; + // if (this.stale > staleValueForSleep) + // _outputValue = Vector3.zero; foreach (Nucleus nucleus in this.nuclei) nucleus.UpdateNuclei(); diff --git a/Editor/ClusterInspector.cs b/Editor/ClusterInspector.cs index e90ba23..1710cb7 100644 --- a/Editor/ClusterInspector.cs +++ b/Editor/ClusterInspector.cs @@ -500,6 +500,7 @@ public class ClusterInspector : Editor { private bool showSynapses = true; private bool showActivation = true; protected bool breakOnWake = false; + protected bool trace = false; void DrawInspector(VisualElement inspectorContainer) { if (inspectorContainer == null) return; @@ -638,6 +639,9 @@ public class ClusterInspector : Editor { if (this.currentNucleus.isSleeping == false) Debug.Break(); } + trace = EditorGUILayout.Toggle("Trace", trace); + this.currentNucleus.trace = trace; + }); inspectorContainer.Add(container); diff --git a/Nucleus.cs b/Nucleus.cs index 01d2a80..b4defa4 100644 --- a/Nucleus.cs +++ b/Nucleus.cs @@ -31,6 +31,7 @@ public abstract class Nucleus { [NonSerialized] public int stale = 1000; public readonly int staleValueForSleep = 20; + public bool trace = false; public abstract Nucleus ShallowCloneTo(Cluster parent); public abstract Nucleus Clone(ClusterPrefab prefab); @@ -101,6 +102,9 @@ public abstract class Nucleus { public abstract void UpdateStateIsolated(); public virtual void UpdateNuclei() { + if (this.array == null || this.array.nuclei == null || this.array.nuclei.Length <= 1) + return; + this.stale++; if (this.stale > staleValueForSleep) { //Debug.Log($"{this.name} goes to sleep, stale = {this.stale}"); diff --git a/NucleusArray.cs b/NucleusArray.cs index 89ae516..b41aa68 100644 --- a/NucleusArray.cs +++ b/NucleusArray.cs @@ -127,7 +127,8 @@ public class NucleusArray { if (selectedReceiver is Neuron selectedNucleus) selectedNucleus.ProcessStimulus(inputValue); - selectedReceiver.parent.UpdateStateIsolated(); + //selectedReceiver.parent.UpdateStateIsolated(); + selectedReceiver.parent.UpdateFromNucleus(selectedReceiver); } private void CleanupReceivers() {