Add topological evaluation order

This commit is contained in:
Pascal Serrarens 2026-01-30 12:05:44 +01:00
parent 9ae3607911
commit b4f8e5a4d8
5 changed files with 73 additions and 35 deletions

View File

@ -15,41 +15,45 @@ public class Cluster : INucleus {
set => _name = value;
}
public ClusterPrefab storedPrefab;
#region Init
public Cluster(ClusterPrefab prefab, Cluster parent) {
this.prefab = prefab;
this.name = prefab.name;
public Cluster(Cluster parent, ClusterPrefab realPrefab) {
this.storedPrefab = realPrefab;
this.parent = parent;
this.parent?.nuclei.Add(this);
ClonePrefab();
this.sortedNuclei = TopologicalSort(this.nuclei);
}
public Cluster(ClusterPrefab realPrefab) {
this.storedPrefab = realPrefab;
this.name = realPrefab.name;
this.cluster = null;
if (this.cluster != null)
this.cluster.nuclei.Add(this);
ClonePrefab();
}
public Cluster(ClusterPrefab parent, ClusterPrefab realPrefab) {
this.storedPrefab = realPrefab;
//this.prefab = realPrefab.Clone();
this.name = realPrefab.name;
public Cluster(ClusterPrefab prefab, ClusterPrefab parent = null) {
this.prefab = prefab;
this.name = prefab.name;
this.cluster = parent;
if (this.cluster != null)
this.cluster.nuclei.Add(this);
ClonePrefab();
this.sortedNuclei = TopologicalSort(this.nuclei);
}
// public Cluster(ClusterPrefab parent, ClusterPrefab realPrefab) {
// this.prefab = realPrefab;
// this.name = realPrefab.name;
// this.cluster = parent;
// if (this.cluster != null)
// this.cluster.nuclei.Add(this);
// ClonePrefab();
// }
private void ClonePrefab() {
IReceptor[] nucleiArray = this.storedPrefab.nuclei.ToArray();
IReceptor[] nucleiArray = this.prefab.nuclei.ToArray();
// first clone the nuclei without their connections
foreach (IReceptor nucleus in this.storedPrefab.nuclei)
foreach (IReceptor nucleus in this.prefab.nuclei)
nucleus.ShallowCloneTo(this);
IReceptor[] clonedNuclei = this.nuclei.ToArray();
@ -83,6 +87,43 @@ public class Cluster : INucleus {
}
}
// Sort the nuclei in a correct evaluation order
private List<IReceptor> TopologicalSort(List<IReceptor> nodes) {
Dictionary<IReceptor, int> inDegree = new();
foreach (IReceptor node in nodes)
inDegree[node] = 0; // Initialize in-degree to zero
// Calculate in-degrees
foreach (IReceptor node in nodes) {
foreach (INucleus receiver in node.receivers)
inDegree[receiver]++;
}
Queue<IReceptor> queue = new();
foreach (IReceptor node in nodes) {
if (inDegree[node] == 0) // Nodes with no dependencies
queue.Enqueue(node);
}
List<IReceptor> sortedOrder = new();
while (queue.Count > 0) {
IReceptor current = queue.Dequeue();
sortedOrder.Add(current); // Process the node
foreach (INucleus receiver in current.receivers) {
inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver);
}
}
// Check for cycles in the graph
if (sortedOrder.Count != nodes.Count)
throw new InvalidOperationException("Graph is not a DAG; a cycle exists.");
return sortedOrder;
}
public virtual IReceptor Clone() {
Neuron clone = new(this.cluster, this.name) {
array = this.array,
@ -99,7 +140,7 @@ public class Cluster : INucleus {
}
public IReceptor ShallowCloneTo(Cluster parent) {
Cluster clone = new(parent, this.storedPrefab) {
Cluster clone = new(this.prefab, parent) {
name = this.name,
};
return clone;
@ -113,11 +154,18 @@ public class Cluster : INucleus {
return -1;
}
#endregion Init
public ClusterPrefab prefab;
public ClusterPrefab cluster { get; set; }
public Cluster parent { get; set; }
[SerializeReference]
public List<IReceptor> nuclei = new();
// the nuclei sorted using topological sorting
// to ensure that the cluster is computer in the right order
public List<IReceptor> sortedNuclei;
public List<INucleus> _inputs = null;
public virtual List<INucleus> inputs {

View File

@ -29,10 +29,6 @@ public class MemoryCell : Neuron {
//Applying the weight factgors
foreach (Synapse synapse in this.synapses) {
if (synapse.nucleus == this) {
float deltaTime = Time.time - this.lastTime;
synapse.weight = deltaTime;
}
result += synapse.weight * synapse.nucleus.outputValue;
if (lengthsq(synapse.nucleus.outputValue) != 0)
n++;

View File

@ -154,7 +154,6 @@ public class Neuron : INucleus {
// private bool _isSleeping = false;
// public bool isSleeping => _isSleeping;
public bool isSleeping => lengthsq(this.outputValue) == 0;
public float lastTime { get; private set; }
public void UpdateNuclei() {
this.stale++;
@ -292,10 +291,6 @@ public class Neuron : INucleus {
//Applying the weight factgors
foreach (Synapse synapse in this.synapses) {
if (synapse.nucleus == this) {
float deltaTime = Time.time - this.lastTime;
synapse.weight = deltaTime;
}
sum += synapse.weight * synapse.nucleus.outputValue;
// Perhaps synapses should be removed when the output value goes to 0....
@ -340,7 +335,6 @@ public class Neuron : INucleus {
Debug.Log($"{this.parent.name}.{this.name}: {this.outputValue}");
}
this.lastTime = Time.time;
foreach (INucleus receiver in this.receivers)
receiver.UpdateState();

View File

@ -621,7 +621,7 @@ public class ClusterInspector : Editor {
}
private void OnClusterPicked(INucleus nucleus, ClusterPrefab prefab) {
Cluster subclusterInstance = new(this.cluster, prefab);
Cluster subclusterInstance = new(prefab, this.cluster);
subclusterInstance.AddReceiver(nucleus);
// This does not work somehow
// this.currentNucleus = subclusterInstance;
@ -630,9 +630,9 @@ public class ClusterInspector : Editor {
private void EditCluster(Cluster subCluster) {
// May be used with storedPrefab...
Selection.activeObject = subCluster.storedPrefab;
EditorGUIUtility.PingObject(subCluster.storedPrefab);
var editor = Editor.CreateEditor(subCluster.storedPrefab);
Selection.activeObject = subCluster.prefab;
EditorGUIUtility.PingObject(subCluster.prefab);
var editor = Editor.CreateEditor(subCluster.prefab);
}
// Connect to another nucleus in the same cluster

View File

@ -79,7 +79,7 @@ public class NanoBrainComponent_Editor : Editor {
});
if (brain != null && board != null)
board.SetGraph(component.gameObject, brain.storedPrefab, brain.output, inspectorContainer);
board.SetGraph(component.gameObject, brain.prefab, brain.output, inspectorContainer);
// else
// Debug.LogWarning(" No brain!");