Fix multiple pheromone placement

This commit is contained in:
Pascal Serrarens 2026-02-10 17:34:03 +01:00
parent c011e04650
commit 360346eeac
4 changed files with 131 additions and 5 deletions

View File

@ -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<Nucleus> 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<Nucleus, List<Nucleus>> computeOrders = new();
private void ComputeOrders() {
foreach (Nucleus input in this._inputs) {
computeOrders[input] = TopologicalSort2(input);
}
}
private List<Nucleus> TopologicalSort2(Nucleus startNode) {
Dictionary<Nucleus, int> inDegree = new Dictionary<Nucleus, int>();
HashSet<Nucleus> visited = new HashSet<Nucleus>();
// 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<Nucleus> queue = new Queue<Nucleus>();
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<Nucleus> sortedOrder = new List<Nucleus>();
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<Nucleus> TopologicalSort3(Nucleus startNode) {
Dictionary<Nucleus, int> 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<Nucleus> queue = new();
queue.Enqueue(startNode);
List<Nucleus> 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<Nucleus> 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();

View File

@ -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);

View File

@ -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}");

View File

@ -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() {