diff --git a/Editor/ClusterEditor.cs b/Editor/ClusterEditor.cs index 9ca0a95..725b27d 100644 --- a/Editor/ClusterEditor.cs +++ b/Editor/ClusterEditor.cs @@ -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; } } diff --git a/Editor/ClusterViewer.cs b/Editor/ClusterViewer.cs index d6c4c9f..5f11269 100644 --- a/Editor/ClusterViewer.cs +++ b/Editor/ClusterViewer.cs @@ -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) { diff --git a/Runtime/Scripts/Core/Cluster.cs b/Runtime/Scripts/Core/Cluster.cs index def7228..87366f3 100644 --- a/Runtime/Scripts/Core/Cluster.cs +++ b/Runtime/Scripts/Core/Cluster.cs @@ -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> 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 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 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 TopologicalSort2(Nucleus startNode) { Dictionary inDegree = new(); - HashSet visited = new(); - - // Initialize in-degrees and mark all nodes as unvisited - foreach (Nucleus node in this.clusterNuclei) - inDegree[node] = 0; + //HashSet visited = new(); // Calculate in-degrees for all nodes reachable from the start node - Queue queue = new Queue(); + Queue 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 sortedOrder = new List(); + List 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 CollectSynapsesTo(Cluster otherCluster) { + List 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 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(); } diff --git a/Runtime/Scripts/Core/Synapse.cs b/Runtime/Scripts/Core/Synapse.cs index e71130d..9e685c4 100644 --- a/Runtime/Scripts/Core/Synapse.cs +++ b/Runtime/Scripts/Core/Synapse.cs @@ -29,11 +29,7 @@ namespace NanoBrain { this.weight = weight; } - public bool isSleeping { - get { - return this.neuron.isSleeping; - } - } + public bool isSleeping => this.neuron.isSleeping; } } \ No newline at end of file