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;
set {
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
string neuronName = synapse.neuron.name;
if (synapse.neuron.parent != null)
if (synapse.neuron.parent != null)
neuronName = synapse.neuron.parent.baseName + "." + neuronName;
if (drawnNeuronNames.Contains(neuronName))
@ -429,7 +429,7 @@ namespace NanoBrain {
// Draw multiple synapses to the same neuron only once
string neuronName = synapse.neuron.name;
if (synapse.neuron.parent != null)
if (synapse.neuron.parent != null)
neuronName = synapse.neuron.parent.baseName + "." + neuronName;
if (drawnNeuronNames.Contains(neuronName))
@ -459,17 +459,30 @@ namespace NanoBrain {
// Hack to disable showing labels
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
float spacing = 400f / nucleus.parent.instanceCount;
float margin = 10 + spacing / 2;
int row = 0;
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);
DrawEdge(parentPos, position);
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) {
alignment = TextAnchor.UpperCenter,
normal = { textColor = Color.white },
@ -784,8 +797,8 @@ namespace NanoBrain {
// expandArray = false;
// }
// else {
if (nucleus is Cluster cluster)
OnClusterClick(cluster);
if (nucleus is Cluster cluster)
OnClusterClick(cluster);
// }
}
else if (nucleus.parent != null && this.currentNucleus != null && nucleus.parent != this.currentNucleus.parent) {

View File

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

View File

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