Viewer for multi-clusters

This commit is contained in:
Pascal Serrarens 2026-05-04 14:08:49 +02:00
parent 04010903f5
commit af0ba68bd8
2 changed files with 109 additions and 59 deletions

View File

@ -22,6 +22,8 @@ namespace NanoBrain {
} }
//protected Nucleus currentNucleus; //protected Nucleus currentNucleus;
protected Nucleus selectedOutput; protected Nucleus selectedOutput;
// Only used when selecting a synapse to a multi-cluster
protected Nucleus selectedSynapseNeuron;
protected GameObject gameObject; protected GameObject gameObject;
private bool expandArray = false; private bool expandArray = false;
@ -382,6 +384,10 @@ namespace NanoBrain {
} }
protected void DrawSynapses(Nucleus nucleus, Vector3 parentPos, float size) { protected void DrawSynapses(Nucleus nucleus, Vector3 parentPos, float size) {
if (this.selectedSynapseNeuron != null) {
DrawClusterSynapses(this.selectedSynapseNeuron, parentPos, size);
return;
}
if (nucleus == null) if (nucleus == null)
return; return;
@ -389,15 +395,19 @@ namespace NanoBrain {
// This is used to 'scale' the output value colors of the nuclei // This is used to 'scale' the output value colors of the nuclei
float maxValue = 0; float maxValue = 0;
int neuronCount = 0; int neuronCount = 0;
List<Neuron> drawnNeurons = new(); List<string> drawnNeuronNames = new();
foreach (Synapse synapse in nucleus.synapses) { foreach (Synapse synapse in nucleus.synapses) {
if (synapse.neuron == null) if (synapse.neuron == null)
continue; continue;
// Count multiple synapses to the same neuron only once // Count multiple synapses to the same neuron only once
if (drawnNeurons.Contains(synapse.neuron)) string neuronName = synapse.neuron.name;
if (synapse.neuron.parent != null)
neuronName = synapse.neuron.parent.baseName + "." + neuronName;
if (drawnNeuronNames.Contains(neuronName))
continue; continue;
drawnNeurons.Add(synapse.neuron); drawnNeuronNames.Add(neuronName);
float value = synapse.neuron.outputMagnitude * synapse.weight; float value = synapse.neuron.outputMagnitude * synapse.weight;
if (value > maxValue) if (value > maxValue)
@ -411,15 +421,20 @@ namespace NanoBrain {
float margin = 10 + spacing / 2; float margin = 10 + spacing / 2;
int row = 0; int row = 0;
drawnNeurons = new(); //List<Neuron> drawnNeurons = new();
drawnNeuronNames = new();
foreach (Synapse synapse in nucleus.synapses) { foreach (Synapse synapse in nucleus.synapses) {
if (synapse.neuron is null) if (synapse.neuron is null)
continue; continue;
// Draw multiple synapses to the same neuron only once // Draw multiple synapses to the same neuron only once
if (drawnNeurons.Contains(synapse.neuron)) string neuronName = synapse.neuron.name;
if (synapse.neuron.parent != null)
neuronName = synapse.neuron.parent.baseName + "." + neuronName;
if (drawnNeuronNames.Contains(neuronName))
continue; continue;
drawnNeurons.Add(synapse.neuron); drawnNeuronNames.Add(neuronName);
Vector3 pos = new(250, margin + row * spacing, 0.0f); Vector3 pos = new(250, margin + row * spacing, 0.0f);
DrawEdge(parentPos, pos); DrawEdge(parentPos, pos);
@ -437,6 +452,37 @@ namespace NanoBrain {
} }
} }
protected void DrawClusterSynapses(Nucleus nucleus, Vector3 parentPos, float size) {
if (nucleus == null || nucleus.parent == null || nucleus.parent.siblingClusters == null)
return;
// Hack to disable showing labels
expandArray = true;
// 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);
Vector3 position = new(250, margin + row * spacing, 0.0f);
DrawEdge(parentPos, position);
Color color = Color.black;
DrawNucleus(siblingNucleus, position, size, color);
GUIStyle style = new(EditorStyles.label) {
alignment = TextAnchor.UpperCenter,
normal = { textColor = Color.white },
fontStyle = FontStyle.Bold,
};
Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron
string name = $"{sibling.baseName}.{nucleus.name}";
Handles.Label(labelPos, name, style);
row++;
}
expandArray = false;
}
protected void DrawOutputs(Vector2 parentPos, float size) { protected void DrawOutputs(Vector2 parentPos, float size) {
// Determine the maximum value in this layer // Determine the maximum value in this layer
// This is used to 'scale' the output value colors of the nuclei // This is used to 'scale' the output value colors of the nuclei
@ -536,7 +582,7 @@ namespace NanoBrain {
else if (nucleus is Cluster cluster) else if (nucleus is Cluster cluster)
DrawCluster(cluster, position, color, size); DrawCluster(cluster, position, color, size);
if (expandArray == false || nucleus != currentNucleus) { if (expandArray == false) {// || nucleus != currentNucleus) {
// put name below nucleus // put name below nucleus
Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron
style.alignment = TextAnchor.UpperCenter; style.alignment = TextAnchor.UpperCenter;
@ -730,25 +776,34 @@ namespace NanoBrain {
protected void OnNeuronClick(Nucleus nucleus) { protected void OnNeuronClick(Nucleus nucleus) {
if (nucleus == this.currentNucleus) { if (nucleus == this.currentNucleus) {
if (Application.isPlaying) { this.selectedSynapseNeuron = null;
if (nucleus is Cluster) // if (Application.isPlaying) {
expandArray = !expandArray; // if (nucleus is Cluster)
else // expandArray = !expandArray;
expandArray = false; // else
} // 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) {
// We go to a different cluster // We go to a different cluster
if (Application.isPlaying) { if (Application.isPlaying) {
if (this.selectedSynapseNeuron == null && nucleus.parent.instanceCount > 1) {
this.selectedSynapseNeuron = nucleus;
expandArray = false;
}
else {
this.currentNucleus = nucleus; this.currentNucleus = nucleus;
if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0) if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0)
this.selectedOutput = this.currentNucleus; this.selectedOutput = this.currentNucleus;
this.selectedSynapseNeuron = null;
expandArray = false; expandArray = false;
} }
}
else { else {
// select the cluster, not the neuron in the cluster // select the cluster, not the neuron in the cluster
this.currentNucleus = nucleus.parent; this.currentNucleus = nucleus.parent;

View File

@ -171,20 +171,23 @@ namespace NanoBrain {
if (clonedNucleus is not Cluster clonedCluster) if (clonedNucleus is not Cluster clonedCluster)
continue; continue;
// if (clonedCluster.instanceCount <= 1) List<Cluster> siblings = new() {
// continue; clonedCluster
};
for (int instanceIx = 1; instanceIx < clonedCluster.instanceCount; instanceIx++) { for (int instanceIx = 1; instanceIx < clonedCluster.instanceCount; instanceIx++) {
//ClusterPrefab prefabCluster = clonedCluster.prefab;
// Create another sibling // Create another sibling
Debug.Log($"create {clonedCluster.prefab.name} sibling"); Debug.Log($"create {clonedCluster.prefab.name} sibling");
Cluster sibling = new(clonedCluster.prefab, this) { Cluster sibling = new(clonedCluster.prefab, this) {
name = this.name, name = $"{clonedCluster.baseName}: {instanceIx}",
clusterPrefab = this.clusterPrefab, clusterPrefab = this.clusterPrefab,
instanceCount = this.instanceCount, instanceCount = this.instanceCount,
}; };
RestoreAllExternalReceivers(clonedCluster, clonedCluster.prefab, this); siblings.Add(sibling);
CopyAllExternalReceivers(clonedCluster, sibling, clonedCluster.prefab, this);
} }
Cluster[] siblingClusters = siblings.ToArray();
foreach (Cluster sibling in siblings)
sibling.siblingClusters = siblingClusters;
} }
} }
/* /*
@ -411,44 +414,36 @@ namespace NanoBrain {
return clone; return clone;
} }
private static void RestoreAllExternalReceivers(Cluster clonedCluster, 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];
if (sourceNucleus is not Neuron sourceNeuron)
continue;
// int clonedClusterIx = GetNucleusIndex(clonedParent.clusterNuclei, clonedCluster); if (sibling.clusterNuclei[nucleusIx] is not Neuron clonedNeuron)
// if (prefabParent.nuclei[clonedClusterIx] is not Cluster sourceCluster) continue;
// return;
// for (int nucleusIx = 0; nucleusIx < sourceCluster.clusterNuclei.Count; nucleusIx++) { // copy the receivers (and thus synapses) from the source to the sibling
// Nucleus sourceNucleus = sourceCluster.clusterNuclei[nucleusIx]; foreach (Nucleus receiver in sourceNeuron.receivers) {
// if (sourceNucleus is not Neuron sourceNeuron) int ix = GetNucleusIndex(clonedParent.clusterNuclei, receiver);
// continue; if (ix < 0 || ix >= clonedParent.clusterNuclei.Count)
continue;
// if (clonedCluster.clusterNuclei[nucleusIx] is not Neuron clonedNeuron) // Find the synapse for the weight
// continue; float weight = 1;
foreach (Synapse synapse in receiver.synapses) {
// // copy the receivers (and thus synapses) from the source to the clone // Find the weight for this synapse
// foreach (Nucleus receiver in sourceNeuron.receivers) { if (synapse.neuron == sourceNucleus) {
// int ix = GetNucleusIndex(prefabParent.nuclei, receiver); weight = synapse.weight;
// if (ix < 0 || ix >= clonedParent.clusterNuclei.Count) break;
// continue; }
}
// Nucleus clonedReceiver = clonedParent.clusterNuclei[ix];
// // Find the synapse for the weight
// float weight = 1;
// foreach (Synapse synapse in receiver.synapses) {
// // Find the weight for this synapse
// if (synapse.neuron == sourceNucleus) {
// weight = synapse.weight;
// break;
// }
// }
// clonedNeuron.AddReceiver(clonedReceiver, weight);
// Debug.Log($"external: {clonedReceiver.name} receives from {clonedNeuron.name} {clonedNeuron.GetHashCode()}");
// }
// }
clonedNeuron.AddReceiver(receiver, weight);
Debug.Log($"external: {receiver.name} receives from {clonedNeuron.name} {clonedNeuron.GetHashCode()}");
}
}
} }