multi-cluster calculation fix

This commit is contained in:
Pascal Serrarens 2026-05-04 16:47:58 +02:00
parent af0ba68bd8
commit 7ef8e42e09
4 changed files with 80 additions and 42 deletions

View File

@ -70,7 +70,7 @@ namespace NanoBrain {
get => base.currentNucleus; get => base.currentNucleus;
set { set {
base.currentNucleus = value; base.currentNucleus = value;
this.currentPrefabNucleus = this.prefab.GetNucleus(value.name); this.currentPrefabNucleus = value != null ? this.prefab.GetNucleus(value.name) : null;
} }
} }

View File

@ -402,7 +402,7 @@ namespace NanoBrain {
// Count multiple synapses to the same neuron only once // Count multiple synapses to the same neuron only once
string neuronName = synapse.neuron.name; string neuronName = synapse.neuron.name;
if (synapse.neuron.parent != null) if (synapse.neuron.parent != null)
neuronName = synapse.neuron.parent.baseName + "." + neuronName; neuronName = synapse.neuron.parent.baseName + "." + neuronName;
if (drawnNeuronNames.Contains(neuronName)) if (drawnNeuronNames.Contains(neuronName))
@ -429,7 +429,7 @@ namespace NanoBrain {
// Draw multiple synapses to the same neuron only once // Draw multiple synapses to the same neuron only once
string neuronName = synapse.neuron.name; string neuronName = synapse.neuron.name;
if (synapse.neuron.parent != null) if (synapse.neuron.parent != null)
neuronName = synapse.neuron.parent.baseName + "." + neuronName; neuronName = synapse.neuron.parent.baseName + "." + neuronName;
if (drawnNeuronNames.Contains(neuronName)) if (drawnNeuronNames.Contains(neuronName))
@ -459,17 +459,30 @@ namespace NanoBrain {
// Hack to disable showing labels // Hack to disable showing labels
expandArray = true; expandArray = true;
float maxValue = 0;
foreach (Cluster sibling in nucleus.parent.siblingClusters) {
Neuron siblingNeuron = sibling.GetNucleus(nucleus.name) as Neuron;
float value = siblingNeuron.outputMagnitude; // no need to add weight as they are all the same
if (value > maxValue)
maxValue = value;
}
// Determine the spacing of the nuclei in the layer // Determine the spacing of the nuclei in the layer
float spacing = 400f / nucleus.parent.instanceCount; float spacing = 400f / nucleus.parent.instanceCount;
float margin = 10 + spacing / 2; float margin = 10 + spacing / 2;
int row = 0; int row = 0;
foreach (Cluster sibling in nucleus.parent.siblingClusters) { foreach (Cluster sibling in nucleus.parent.siblingClusters) {
Nucleus siblingNucleus = sibling.GetNucleus(nucleus.name); Neuron siblingNeuron = sibling.GetNucleus(nucleus.name) as Neuron;
Vector3 position = new(250, margin + row * spacing, 0.0f); Vector3 position = new(250, margin + row * spacing, 0.0f);
DrawEdge(parentPos, position); DrawEdge(parentPos, position);
Color color = Color.black; Color color = Color.black;
DrawNucleus(siblingNucleus, position, size, color); if (Application.isPlaying) {
if (maxValue == 0 || !float.IsFinite(maxValue))
maxValue = 1;
float brightness = siblingNeuron.outputMagnitude / maxValue;
color = new Color(brightness, brightness, brightness, 1f);
} DrawNucleus(siblingNeuron, position, size, color);
GUIStyle style = new(EditorStyles.label) { GUIStyle style = new(EditorStyles.label) {
alignment = TextAnchor.UpperCenter, alignment = TextAnchor.UpperCenter,
normal = { textColor = Color.white }, normal = { textColor = Color.white },
@ -784,8 +797,8 @@ namespace NanoBrain {
// expandArray = false; // expandArray = false;
// } // }
// else { // else {
if (nucleus is Cluster cluster) if (nucleus is Cluster cluster)
OnClusterClick(cluster); OnClusterClick(cluster);
// } // }
} }
else if (nucleus.parent != null && this.currentNucleus != null && nucleus.parent != this.currentNucleus.parent) { else if (nucleus.parent != null && this.currentNucleus != null && nucleus.parent != this.currentNucleus.parent) {

View File

@ -414,7 +414,7 @@ namespace NanoBrain {
return clone; return clone;
} }
private static void CopyAllExternalReceivers(Cluster sourceCluster, Cluster sibling, ClusterPrefab prefabParent, Cluster clonedParent) { private static void CopyAllExternalReceivers(Cluster sourceCluster, Cluster sibling, ClusterPrefab prefabParent, Cluster clonedParent) {
for (int nucleusIx = 0; nucleusIx < sourceCluster.clusterNuclei.Count; nucleusIx++) { for (int nucleusIx = 0; nucleusIx < sourceCluster.clusterNuclei.Count; nucleusIx++) {
Nucleus sourceNucleus = sourceCluster.clusterNuclei[nucleusIx]; Nucleus sourceNucleus = sourceCluster.clusterNuclei[nucleusIx];
@ -444,7 +444,7 @@ namespace NanoBrain {
Debug.Log($"external: {receiver.name} receives from {clonedNeuron.name} {clonedNeuron.GetHashCode()}"); Debug.Log($"external: {receiver.name} receives from {clonedNeuron.name} {clonedNeuron.GetHashCode()}");
} }
} }
} }
protected int GetNucleusIndex(Nucleus[] nuclei, Nucleus nucleus) { protected int GetNucleusIndex(Nucleus[] nuclei, Nucleus nucleus) {
@ -539,6 +539,7 @@ namespace NanoBrain {
return cluster; return cluster;
Cluster selectedCluster = SelectCluster(); Cluster selectedCluster = SelectCluster();
selectedCluster.name = baseName + ": " + thingName;
thingClusters[thingId] = selectedCluster; thingClusters[thingId] = selectedCluster;
return selectedCluster; return selectedCluster;
} }
@ -615,22 +616,33 @@ namespace NanoBrain {
public Dictionary<Nucleus, List<Nucleus>> computeOrders = new(); public Dictionary<Nucleus, List<Nucleus>> computeOrders = new();
private void ComputeOrders() { private void ComputeOrders() {
foreach (Nucleus input in this._inputs) foreach (Nucleus nucleus in this.clusterNuclei) {
computeOrders[input] = TopologicalSort2(input); // if (nucleus is Cluster cluster) {
// List<Synapse> synapses = this.CollectSynapsesTo(cluster);
// foreach (Synapse synapse in synapses) {
// computeOrders[synapse.neuron] = TopologicalSort2(synapse.neuron);
// Debug.Log($"{this.baseName}: Order for {cluster.baseName}.{synapse.neuron.name}");
// }
// // List<Nucleus> receivers = cluster.CollectReceivers();
// // foreach (Nucleus receiver in receivers)
// // computeOrders[receiver] = TopologicalSort2(receiver);
// }
// else {
computeOrders[nucleus] = TopologicalSort2(nucleus);
Debug.Log($"{this.baseName} Order for {nucleus.name}");
// }
}
} }
private List<Nucleus> TopologicalSort2(Nucleus startNode) { private List<Nucleus> TopologicalSort2(Nucleus startNode) {
Dictionary<Nucleus, int> inDegree = new(); Dictionary<Nucleus, int> inDegree = new();
HashSet<Nucleus> visited = new(); //HashSet<Nucleus> visited = new();
// Initialize in-degrees and mark all nodes as unvisited
foreach (Nucleus node in this.clusterNuclei)
inDegree[node] = 0;
// Calculate in-degrees for all nodes reachable from the start node // Calculate in-degrees for all nodes reachable from the start node
Queue<Nucleus> queue = new Queue<Nucleus>(); Queue<Nucleus> queue = new();
queue.Enqueue(startNode); queue.Enqueue(startNode);
visited.Add(startNode); //visited.Add(startNode);
inDegree[startNode] = 0;
while (queue.Count > 0) { while (queue.Count > 0) {
Nucleus current = queue.Dequeue(); Nucleus current = queue.Dequeue();
@ -640,25 +652,24 @@ namespace NanoBrain {
else if (current is Cluster cluster) else if (current is Cluster cluster)
receivers = cluster.CollectReceivers(); receivers = cluster.CollectReceivers();
// if (current is Neuron neuron) {
foreach (Nucleus receiver in receivers) { foreach (Nucleus receiver in receivers) {
if (!visited.Contains(receiver)) { if (!inDegree.ContainsKey(receiver)) {
visited.Add(receiver); //visited.Add(receiver);
inDegree[receiver] = 0;
queue.Enqueue(receiver); queue.Enqueue(receiver);
} }
inDegree[receiver]++; inDegree[receiver]++;
} }
// }
} }
// Perform topological sort on all reachable nodes // Perform topological sort on all reachable nodes
queue.Clear(); queue.Clear();
foreach (Nucleus node in visited) { foreach (Nucleus node in inDegree.Keys) {
if (inDegree[node] == 0) if (inDegree[node] == 0)
queue.Enqueue(node); queue.Enqueue(node);
} }
List<Nucleus> sortedOrder = new List<Nucleus>(); List<Nucleus> sortedOrder = new();
while (queue.Count > 0) { while (queue.Count > 0) {
Nucleus current = queue.Dequeue(); Nucleus current = queue.Dequeue();
sortedOrder.Add(current); // Process the node sortedOrder.Add(current); // Process the node
@ -669,21 +680,18 @@ namespace NanoBrain {
else if (current is Cluster cluster) else if (current is Cluster cluster)
receivers = cluster.CollectReceivers(); receivers = cluster.CollectReceivers();
//if (current is Neuron neuron) {
foreach (Nucleus receiver in receivers) { foreach (Nucleus receiver in receivers) {
if (visited.Contains(receiver)) { if (inDegree.ContainsKey(receiver)) {
inDegree[receiver]--; inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver); queue.Enqueue(receiver);
} }
} }
//}
} }
// Check for cycles in the graph // Check for cycles in the graph
if (sortedOrder.Count != visited.Count) // if (sortedOrder.Count != visited.Count)
throw new InvalidOperationException("Graph is not a DAG; a cycle exists."); // throw new InvalidOperationException("Graph is not a DAG; a cycle exists.");
return sortedOrder; return sortedOrder;
} }
@ -782,7 +790,7 @@ namespace NanoBrain {
// Debug.Log($"output {receiver.name}"); // Debug.Log($"output {receiver.name}");
// Only add receivers outside this cluster // Only add receivers outside this cluster
if (receiver.clusterPrefab != this.prefab) { if (receiver.clusterPrefab != this.prefab) {
if (removeDuplicates && receivers.Contains(receiver) == false) if (removeDuplicates == false || receivers.Contains(receiver) == false)
// Debug.Log($" YES"); // Debug.Log($" YES");
receivers.Add(receiver); receivers.Add(receiver);
} }
@ -806,6 +814,19 @@ namespace NanoBrain {
} }
return connections; return connections;
} }
public List<Synapse> CollectSynapsesTo(Cluster otherCluster) {
List<Synapse> collectedSynapses = new();
foreach (Nucleus nucleus in this.clusterNuclei) {
if (nucleus is not Neuron neuron)
continue;
foreach (Synapse synapse in nucleus.synapses) {
if (synapse.neuron.parent == otherCluster)
collectedSynapses.Add(synapse);
}
}
return collectedSynapses;
}
public void MoveReceivers(Cluster newCluster) { public void MoveReceivers(Cluster newCluster) {
Debug.Log($"Move receivers for {this.name} to {newCluster.name}"); Debug.Log($"Move receivers for {this.name} to {newCluster.name}");
@ -841,23 +862,31 @@ namespace NanoBrain {
// no bias+synapse input state calculation for now... // no bias+synapse input state calculation for now...
if (this.computeOrders.ContainsKey(startNucleus) == false) { if (this.computeOrders.ContainsKey(startNucleus) == false) {
//Debug.LogError($"{this.name} compute orders does not contain an order for {startNucleus.name}"); Debug.LogError($"{this.name} compute orders does not contain an order for {startNucleus.name}");
return; return;
} }
List<Nucleus> computeOrder = this.computeOrders[startNucleus]; List<Nucleus> computeOrder = this.computeOrders[startNucleus];
if (startNucleus.trace) //if (startNucleus.trace)
Debug.Log($"Update from {startNucleus.name}"); Debug.Log($"Update from {startNucleus.name}");
foreach (Nucleus nucleus in computeOrder) { foreach (Nucleus nucleus in computeOrder) {
if (nucleus is not Cluster) { if (nucleus is not Cluster) {
nucleus.UpdateStateIsolated(); nucleus.UpdateStateIsolated();
if (startNucleus.trace && nucleus is Neuron neuron) //if (startNucleus.trace && nucleus is Neuron neuron)
Debug.Log($" {nucleus.name}[{nucleus.GetHashCode()}]"); // = {neuron.outputValue}"); Debug.Log($" {nucleus.name}");
if (nucleus is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) {
if (receiver.parent != this) {
Debug.Log($" External: {receiver.parent.name}.{receiver.name}");
receiver.parent.UpdateFromNucleus(receiver);
}
}
}
} }
} }
// continue in parent // continue in parent
this.parent?.UpdateFromNucleus(this); //this.parent?.UpdateFromNucleus(this);
UpdateNuclei(); UpdateNuclei();
} }

View File

@ -29,11 +29,7 @@ namespace NanoBrain {
this.weight = weight; this.weight = weight;
} }
public bool isSleeping { public bool isSleeping => this.neuron.isSleeping;
get {
return this.neuron.isSleeping;
}
}
} }
} }