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 selectedOutput;
// Only used when selecting a synapse to a multi-cluster
protected Nucleus selectedSynapseNeuron;
protected GameObject gameObject;
private bool expandArray = false;
@ -382,6 +384,10 @@ namespace NanoBrain {
}
protected void DrawSynapses(Nucleus nucleus, Vector3 parentPos, float size) {
if (this.selectedSynapseNeuron != null) {
DrawClusterSynapses(this.selectedSynapseNeuron, parentPos, size);
return;
}
if (nucleus == null)
return;
@ -389,15 +395,19 @@ namespace NanoBrain {
// This is used to 'scale' the output value colors of the nuclei
float maxValue = 0;
int neuronCount = 0;
List<Neuron> drawnNeurons = new();
List<string> drawnNeuronNames = new();
foreach (Synapse synapse in nucleus.synapses) {
if (synapse.neuron == null)
continue;
// 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;
drawnNeurons.Add(synapse.neuron);
drawnNeuronNames.Add(neuronName);
float value = synapse.neuron.outputMagnitude * synapse.weight;
if (value > maxValue)
@ -411,15 +421,20 @@ namespace NanoBrain {
float margin = 10 + spacing / 2;
int row = 0;
drawnNeurons = new();
//List<Neuron> drawnNeurons = new();
drawnNeuronNames = new();
foreach (Synapse synapse in nucleus.synapses) {
if (synapse.neuron is null)
continue;
// 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;
drawnNeurons.Add(synapse.neuron);
drawnNeuronNames.Add(neuronName);
Vector3 pos = new(250, margin + row * spacing, 0.0f);
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) {
// Determine the maximum value in this layer
// This is used to 'scale' the output value colors of the nuclei
@ -536,7 +582,7 @@ namespace NanoBrain {
else if (nucleus is Cluster cluster)
DrawCluster(cluster, position, color, size);
if (expandArray == false || nucleus != currentNucleus) {
if (expandArray == false) {// || nucleus != currentNucleus) {
// put name below nucleus
Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron
style.alignment = TextAnchor.UpperCenter;
@ -730,24 +776,33 @@ namespace NanoBrain {
protected void OnNeuronClick(Nucleus nucleus) {
if (nucleus == this.currentNucleus) {
if (Application.isPlaying) {
if (nucleus is Cluster)
expandArray = !expandArray;
else
expandArray = false;
}
else {
this.selectedSynapseNeuron = null;
// if (Application.isPlaying) {
// if (nucleus is Cluster)
// expandArray = !expandArray;
// else
// expandArray = false;
// }
// else {
if (nucleus is Cluster cluster)
OnClusterClick(cluster);
}
// }
}
else if (nucleus.parent != null && this.currentNucleus != null && nucleus.parent != this.currentNucleus.parent) {
// We go to a different cluster
if (Application.isPlaying) {
this.currentNucleus = nucleus;
if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0)
this.selectedOutput = this.currentNucleus;
expandArray = false;
if (this.selectedSynapseNeuron == null && nucleus.parent.instanceCount > 1) {
this.selectedSynapseNeuron = nucleus;
expandArray = false;
}
else {
this.currentNucleus = nucleus;
if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0)
this.selectedOutput = this.currentNucleus;
this.selectedSynapseNeuron = null;
expandArray = false;
}
}
else {
// select the cluster, not the neuron in the cluster

View File

@ -171,20 +171,23 @@ namespace NanoBrain {
if (clonedNucleus is not Cluster clonedCluster)
continue;
// if (clonedCluster.instanceCount <= 1)
// continue;
List<Cluster> siblings = new() {
clonedCluster
};
for (int instanceIx = 1; instanceIx < clonedCluster.instanceCount; instanceIx++) {
//ClusterPrefab prefabCluster = clonedCluster.prefab;
// Create another sibling
Debug.Log($"create {clonedCluster.prefab.name} sibling");
Cluster sibling = new(clonedCluster.prefab, this) {
name = this.name,
name = $"{clonedCluster.baseName}: {instanceIx}",
clusterPrefab = this.clusterPrefab,
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,45 +414,37 @@ namespace NanoBrain {
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 (prefabParent.nuclei[clonedClusterIx] is not Cluster sourceCluster)
// return;
if (sibling.clusterNuclei[nucleusIx] is not Neuron clonedNeuron)
continue;
// for (int nucleusIx = 0; nucleusIx < sourceCluster.clusterNuclei.Count; nucleusIx++) {
// Nucleus sourceNucleus = sourceCluster.clusterNuclei[nucleusIx];
// if (sourceNucleus is not Neuron sourceNeuron)
// continue;
// copy the receivers (and thus synapses) from the source to the sibling
foreach (Nucleus receiver in sourceNeuron.receivers) {
int ix = GetNucleusIndex(clonedParent.clusterNuclei, receiver);
if (ix < 0 || ix >= clonedParent.clusterNuclei.Count)
continue;
// if (clonedCluster.clusterNuclei[nucleusIx] is not Neuron clonedNeuron)
// continue;
// 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;
}
}
// // copy the receivers (and thus synapses) from the source to the clone
// foreach (Nucleus receiver in sourceNeuron.receivers) {
// int ix = GetNucleusIndex(prefabParent.nuclei, receiver);
// if (ix < 0 || ix >= clonedParent.clusterNuclei.Count)
// 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()}");
}
}
}
protected int GetNucleusIndex(Nucleus[] nuclei, Nucleus nucleus) {