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; 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 = parent;
this.parent?.nuclei.Add(this); this.parent?.nuclei.Add(this);
ClonePrefab(); ClonePrefab();
this.sortedNuclei = TopologicalSort(this.nuclei);
} }
public Cluster(ClusterPrefab realPrefab) { public Cluster(ClusterPrefab prefab, ClusterPrefab parent = null) {
this.storedPrefab = realPrefab; this.prefab = prefab;
this.name = realPrefab.name; this.name = prefab.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;
this.cluster = parent; this.cluster = parent;
if (this.cluster != null) if (this.cluster != null)
this.cluster.nuclei.Add(this); this.cluster.nuclei.Add(this);
ClonePrefab(); 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() { private void ClonePrefab() {
IReceptor[] nucleiArray = this.storedPrefab.nuclei.ToArray(); IReceptor[] nucleiArray = this.prefab.nuclei.ToArray();
// first clone the nuclei without their connections // first clone the nuclei without their connections
foreach (IReceptor nucleus in this.storedPrefab.nuclei) foreach (IReceptor nucleus in this.prefab.nuclei)
nucleus.ShallowCloneTo(this); nucleus.ShallowCloneTo(this);
IReceptor[] clonedNuclei = this.nuclei.ToArray(); 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() { public virtual IReceptor Clone() {
Neuron clone = new(this.cluster, this.name) { Neuron clone = new(this.cluster, this.name) {
array = this.array, array = this.array,
@ -99,7 +140,7 @@ public class Cluster : INucleus {
} }
public IReceptor ShallowCloneTo(Cluster parent) { public IReceptor ShallowCloneTo(Cluster parent) {
Cluster clone = new(parent, this.storedPrefab) { Cluster clone = new(this.prefab, parent) {
name = this.name, name = this.name,
}; };
return clone; return clone;
@ -113,11 +154,18 @@ public class Cluster : INucleus {
return -1; return -1;
} }
#endregion Init
public ClusterPrefab prefab;
public ClusterPrefab cluster { get; set; } public ClusterPrefab cluster { get; set; }
public Cluster parent { get; set; } public Cluster parent { get; set; }
[SerializeReference] [SerializeReference]
public List<IReceptor> nuclei = new(); 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 List<INucleus> _inputs = null;
public virtual List<INucleus> inputs { public virtual List<INucleus> inputs {

View File

@ -29,10 +29,6 @@ public class MemoryCell : Neuron {
//Applying the weight factgors //Applying the weight factgors
foreach (Synapse synapse in this.synapses) { 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; result += synapse.weight * synapse.nucleus.outputValue;
if (lengthsq(synapse.nucleus.outputValue) != 0) if (lengthsq(synapse.nucleus.outputValue) != 0)
n++; n++;

View File

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

View File

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

View File

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