Merge commit 'dd326823a8256f3ddb808e071d98c4aede72e410'
This commit is contained in:
commit
ec2a6b7ae9
@ -64,30 +64,22 @@ namespace NanoBrain {
|
||||
public class GraphEditor : GraphView {
|
||||
|
||||
protected ClusterPrefab prefab;
|
||||
protected Nucleus currentPrefabNucleus;
|
||||
|
||||
protected override Nucleus currentNucleus {
|
||||
get => base.currentNucleus;
|
||||
set {
|
||||
base.currentNucleus = value;
|
||||
this.currentPrefabNucleus = value != null ? this.prefab.GetNucleus(value.name) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public GraphEditor(ClusterPrefab prefab) : base(prefab.output.parent) {
|
||||
this.prefab = prefab;
|
||||
|
||||
// In a Prefab editor, no instance exists but we need it for the ClusterViewer.
|
||||
// So we create a temporary instance
|
||||
Cluster cluster = new(prefab);
|
||||
this.currentCluster = cluster;
|
||||
|
||||
Button addButton = new(() => OnAddClusterOutput()) {
|
||||
text = "Add"
|
||||
};
|
||||
topMenuContainer?.Add(addButton);
|
||||
|
||||
Add(topMenuContainer);
|
||||
}
|
||||
|
||||
void OnAddClusterOutput() {
|
||||
Nucleus newOutput = new Neuron(this.prefab, "New Output");
|
||||
this.prefab.RefreshOutputs();
|
||||
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
||||
// outputsPopup.value = newOutput.name;
|
||||
|
||||
this.currentNucleus = newOutput;
|
||||
this.currentCluster = new(prefab);
|
||||
}
|
||||
|
||||
public void SetGraph(GameObject gameObject, VisualElement inspectorContainer) {
|
||||
@ -151,69 +143,76 @@ namespace NanoBrain {
|
||||
if (serializedObject == null || serializedObject.targetObject == null)
|
||||
return;
|
||||
|
||||
if (this.currentNucleus == null)
|
||||
return;
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
margin = new RectOffset(10, 0, 4, 4)
|
||||
};
|
||||
GUIStyle boldTextFieldStyle = new(EditorStyles.textField) {
|
||||
fontStyle = FontStyle.Bold
|
||||
};
|
||||
|
||||
// Nucleus type
|
||||
string nucleusType = this.currentNucleus.GetType().Name;
|
||||
GUILayout.Label(nucleusType, headerStyle);
|
||||
|
||||
// Nucleus name
|
||||
if (this.currentNucleus.parent is Cluster parentCluster) {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button(this.currentNucleus.parent.name))
|
||||
OnClusterClick(parentCluster);
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
if (GUILayout.Button("Reimport"))
|
||||
ReimportCluster(parentCluster);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (this.currentNucleus == null) {
|
||||
OutputsInspector(ref anythingChanged);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
||||
if (newName != this.currentNucleus.name) {
|
||||
this.currentNucleus.name = newName;
|
||||
this.prefab.RefreshOutputs();
|
||||
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
||||
anythingChanged = true;
|
||||
}
|
||||
}
|
||||
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
margin = new RectOffset(10, 0, 4, 4)
|
||||
};
|
||||
// Nucleus type
|
||||
string nucleusType = this.currentNucleus.GetType().Name;
|
||||
GUILayout.Label(nucleusType, headerStyle);
|
||||
|
||||
// Current output value
|
||||
if (Application.isPlaying) {
|
||||
if (currentNucleus is Neuron currentNeuron1) {
|
||||
GUIContent nameLabel = new("Output", currentNeuron1.outputValue.ToString());
|
||||
EditorGUILayout.FloatField(nameLabel, currentNeuron1.outputMagnitude);
|
||||
// Nucleus name
|
||||
Cluster cluster = this.currentPrefabNucleus as Cluster;
|
||||
if (cluster != null) {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button(this.currentNucleus.parent.name))
|
||||
OnClusterClick(cluster);
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
if (GUILayout.Button("Reimport"))
|
||||
ReimportCluster(cluster);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else {
|
||||
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
||||
if (newName != this.currentNucleus.name) {
|
||||
Nucleus prefabNucleus = this.prefab.GetNucleus(this.currentNucleus.name);
|
||||
prefabNucleus.name = newName;
|
||||
// This changes it in the temporary cluster instance
|
||||
this.currentNucleus.name = newName;
|
||||
this.prefab.RefreshOutputs();
|
||||
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
||||
anythingChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Current output value
|
||||
if (Application.isPlaying) {
|
||||
if (currentNucleus is Neuron currentNeuron1) {
|
||||
GUIContent nameLabel = new("Output", currentNeuron1.outputValue.ToString());
|
||||
EditorGUILayout.FloatField(nameLabel, currentNeuron1.outputMagnitude);
|
||||
}
|
||||
else
|
||||
EditorGUILayout.LabelField(" ");
|
||||
}
|
||||
else
|
||||
EditorGUILayout.LabelField(" ");
|
||||
|
||||
// Memory cell
|
||||
if (this.currentNucleus is MemoryCell memory)
|
||||
MemoryCellInspector(memory, ref anythingChanged);
|
||||
// Cluster
|
||||
else if (cluster != null)
|
||||
ClusterInspector(cluster, ref anythingChanged);
|
||||
// Other
|
||||
else
|
||||
NucleusInspector(this.currentNucleus, ref anythingChanged);
|
||||
|
||||
if (GUILayout.Button("Delete"))
|
||||
DeleteNucleus(this.currentNucleus);
|
||||
}
|
||||
else
|
||||
EditorGUILayout.LabelField(" ");
|
||||
|
||||
// Memory cell
|
||||
if (this.currentNucleus is MemoryCell memory)
|
||||
MemoryCellInspector(memory, ref anythingChanged);
|
||||
// Cluster
|
||||
else if (this.currentNucleus is Cluster cluster)
|
||||
ClusterInspector(cluster, ref anythingChanged);
|
||||
// Other
|
||||
else
|
||||
NucleusInspector(this.currentNucleus, ref anythingChanged);
|
||||
|
||||
if (GUILayout.Button("Delete"))
|
||||
DeleteNucleus(this.currentNucleus);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
if (anythingChanged) {
|
||||
@ -222,6 +221,24 @@ namespace NanoBrain {
|
||||
}
|
||||
}
|
||||
|
||||
protected void OutputsInspector(ref bool anythingChanged) {
|
||||
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
margin = new RectOffset(10, 0, 4, 4)
|
||||
};
|
||||
GUILayout.Label("Outputs", headerStyle);
|
||||
|
||||
bool connecting = GUILayout.Button("Add Output Neuron");
|
||||
if (connecting) {
|
||||
Nucleus newOutput = new Neuron(this.prefab, "New Output");
|
||||
// Regenerate the temporary clsuter instance
|
||||
// See also the constructor
|
||||
this.currentCluster = new(this.prefab);
|
||||
this.currentNucleus = newOutput;
|
||||
this.selectedOutput = this.currentNucleus;
|
||||
}
|
||||
}
|
||||
|
||||
protected void MemoryCellInspector(MemoryCell memoryCell, ref bool anythingChanged) {
|
||||
memoryCell.staticMemory = EditorGUILayout.Toggle("Static Memory", memoryCell.staticMemory);
|
||||
NucleusInspector(memoryCell, ref anythingChanged);
|
||||
@ -241,7 +258,6 @@ namespace NanoBrain {
|
||||
|
||||
if (GUILayout.Button("Add")) {
|
||||
Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name);
|
||||
//cluster.AddInstance(this.prefab);
|
||||
cluster.AddInstance();
|
||||
anythingChanged = true;
|
||||
}
|
||||
@ -284,14 +300,17 @@ namespace NanoBrain {
|
||||
EditorGUIUtility.labelWidth = 100;
|
||||
|
||||
Vector3 newBias = EditorGUILayout.Vector3Field("Bias", this.currentNucleus.bias);
|
||||
anythingChanged |= newBias != this.currentNucleus.bias;
|
||||
this.currentNucleus.bias = newBias;
|
||||
if (newBias != this.currentPrefabNucleus.bias) {
|
||||
anythingChanged |= newBias != this.currentNucleus.bias;
|
||||
this.currentPrefabNucleus.bias = newBias;
|
||||
this.currentNucleus.bias = newBias;
|
||||
}
|
||||
EditorGUIUtility.labelWidth = previousLabelWidth;
|
||||
|
||||
Nucleus[] array = null;
|
||||
int elementIx = -1;
|
||||
if (this.currentNucleus.synapses.Count > 0) {
|
||||
Synapse[] synapses = this.currentNucleus.synapses.ToArray();
|
||||
if (this.currentPrefabNucleus.synapses.Count > 0) {
|
||||
Synapse[] synapses = this.currentPrefabNucleus.synapses.ToArray();
|
||||
foreach (Synapse synapse in synapses) {
|
||||
if (synapse.neuron == null)
|
||||
continue;
|
||||
@ -330,24 +349,20 @@ namespace NanoBrain {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (synapse.neuron.clusterPrefab != this.currentNucleus.clusterPrefab) {
|
||||
// If it is a cluster
|
||||
// If it is a different cluster
|
||||
GUIStyle labelStyle = new(GUI.skin.label);
|
||||
float labelWidth = 200;
|
||||
if (synapse.neuron.clusterPrefab != null) {
|
||||
labelWidth = labelStyle.CalcSize(new GUIContent($"{synapse.neuron.clusterPrefab.name}.")).x;
|
||||
GUILayout.Label($"{synapse.neuron.clusterPrefab.name}", GUILayout.Width(labelWidth));
|
||||
}
|
||||
//string[] options = synapse.neuron.parent.clusterNuclei.Select(n => n.name).ToArray();
|
||||
string[] options = synapse.neuron.clusterPrefab.nuclei.Select(n => n.name).ToArray();
|
||||
int selectedIndex = System.Array.IndexOf(options, synapse.neuron.name);
|
||||
int newIndex = EditorGUILayout.Popup(selectedIndex, options);
|
||||
// if (newIndex != selectedIndex && synapse.neuron.clusterPrefab.nuclei[newIndex] is Neuron newNeuron)
|
||||
// ChangeSynapse(synapse, newNeuron);
|
||||
if (newIndex != selectedIndex) {
|
||||
// It shall be ensured that the parent.clusterNuclei and
|
||||
// clusterPrefab.nuclei contain the same neurons in the same order....
|
||||
Nucleus selectedNucleus = synapse.neuron.parent.clusterNuclei[newIndex];
|
||||
Neuron newNeuron = selectedNucleus as Neuron;
|
||||
// Nucleus selectedNucleus = synapse.neuron.parent.clusterNuclei[newIndex];
|
||||
// Neuron newNeuron = selectedNucleus as Neuron;
|
||||
Neuron newNeuron = synapse.neuron.clusterPrefab.nuclei[newIndex] as Neuron;
|
||||
ChangeSynapse(synapse, newNeuron);
|
||||
}
|
||||
}
|
||||
@ -367,14 +382,6 @@ namespace NanoBrain {
|
||||
EditorGUI.indentLevel++;
|
||||
float newWeight = EditorGUILayout.FloatField("Weight", synapse.weight);
|
||||
if (newWeight != synapse.weight) {
|
||||
// if (synapse.neuron.parent is IReceptor receptor) {
|
||||
// Nucleus[] receptorArray = receptor.nucleiArray;
|
||||
// foreach (Synapse s in this.currentNucleus.synapses) {
|
||||
// if (s.neuron.parent is IReceptor r && r.nucleiArray == receptorArray)
|
||||
// s.weight = newWeight;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
synapse.weight = newWeight;
|
||||
anythingChanged = true;
|
||||
}
|
||||
@ -430,15 +437,6 @@ namespace NanoBrain {
|
||||
case Nucleus.Type.Cluster:
|
||||
AddClusterInput(nucleus);
|
||||
break;
|
||||
// case Nucleus.Type.Receptor:
|
||||
// AddReceptorInput(nucleus);
|
||||
// break;
|
||||
// case Nucleus.Type.ClusterReceptor:
|
||||
// AddClusterReceptorInput(nucleus);
|
||||
// break;
|
||||
// case Nucleus.Type.ClusterArray:
|
||||
// AddClusterArrayInput(nucleus);
|
||||
// break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -535,17 +533,15 @@ namespace NanoBrain {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.prefab.nuclei.Remove(nucleus);
|
||||
this.currentCluster.DeleteNucleus(nucleus);//clusterNuclei.Remove(nucleus);
|
||||
|
||||
// if (outputsPopup.value == nucleus.name) {
|
||||
// this.prefab.RefreshOutputs();
|
||||
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
||||
// outputsPopup.index = 0;
|
||||
// }
|
||||
// this.prefab.nuclei.Remove(nucleus);
|
||||
// Neuron.Delete(nucleus);
|
||||
this.prefab.RefreshOutputs();
|
||||
|
||||
Neuron.Delete(nucleus);
|
||||
|
||||
this.currentNucleus = this.prefab.output;
|
||||
this.selectedOutput = this.currentNucleus;
|
||||
}
|
||||
|
||||
Nucleus.Type selectedType = Nucleus.Type.None;
|
||||
@ -570,7 +566,7 @@ namespace NanoBrain {
|
||||
}
|
||||
|
||||
protected virtual void ChangeSynapse(Synapse synapse, Neuron newNucleus) {
|
||||
Neuron synapseNeuron = synapse.neuron as Neuron;
|
||||
Neuron synapseNeuron = synapse.neuron;
|
||||
if (synapse.neuron.parent is Cluster subCluster && subCluster.prefab != this.prefab) {
|
||||
// if (synapse.neuron.parent is ClusterReceptor receptor) {
|
||||
// // the new nucleus is part of a (cluster) receptor,
|
||||
@ -600,8 +596,8 @@ namespace NanoBrain {
|
||||
// }
|
||||
// else {
|
||||
// it is a neuron in a subcluster
|
||||
synapseNeuron.RemoveReceiver(this.currentNucleus);
|
||||
newNucleus.AddReceiver(this.currentNucleus);
|
||||
synapseNeuron.RemoveReceiver(this.currentPrefabNucleus);
|
||||
newNucleus.AddReceiver(this.currentPrefabNucleus);
|
||||
// }
|
||||
}
|
||||
else {
|
||||
|
||||
@ -15,8 +15,15 @@ namespace NanoBrain {
|
||||
//protected readonly ClusterPrefab prefab;
|
||||
protected Cluster currentCluster;
|
||||
protected SerializedObject serializedBrain;
|
||||
protected Nucleus currentNucleus;
|
||||
protected Nucleus _currentNucleus;
|
||||
protected virtual Nucleus currentNucleus {
|
||||
get => _currentNucleus;
|
||||
set => _currentNucleus = value;
|
||||
}
|
||||
//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;
|
||||
@ -324,7 +331,7 @@ namespace NanoBrain {
|
||||
if (nucleus is Neuron neuron)
|
||||
receivers = neuron.receivers;
|
||||
else if (nucleus is Cluster cluster)
|
||||
receivers = cluster.CollectReceivers();
|
||||
receivers = cluster.CollectReceivers(true);
|
||||
else
|
||||
return;
|
||||
|
||||
@ -354,7 +361,6 @@ namespace NanoBrain {
|
||||
float margin = 10 + spacing / 2;
|
||||
|
||||
int row = 0;
|
||||
List<Nucleus[]> drawnArrays = new();
|
||||
foreach (Nucleus receiver in receivers) {
|
||||
Nucleus receiverNucleus = receiver;
|
||||
if (receiverNucleus == null)
|
||||
@ -380,6 +386,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;
|
||||
|
||||
@ -387,15 +397,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)
|
||||
@ -409,15 +423,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);
|
||||
@ -435,6 +454,50 @@ 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;
|
||||
|
||||
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) {
|
||||
Neuron siblingNeuron = sibling.GetNucleus(nucleus.name) as Neuron;
|
||||
Vector3 position = new(250, margin + row * spacing, 0.0f);
|
||||
DrawEdge(parentPos, position);
|
||||
Color color = Color.black;
|
||||
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 },
|
||||
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
|
||||
@ -534,7 +597,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;
|
||||
@ -728,24 +791,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 {
|
||||
if (nucleus is Cluster cluster)
|
||||
OnClusterClick(cluster);
|
||||
}
|
||||
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
|
||||
|
||||
@ -32,9 +32,11 @@ namespace NanoBrain {
|
||||
}
|
||||
|
||||
// This should not be serialized
|
||||
[SerializeReference]
|
||||
//[SerializeReference]
|
||||
[NonSerialized]
|
||||
public Cluster[] siblingClusters;
|
||||
// This serialization should be enough
|
||||
[SerializeField]
|
||||
public int instanceCount = 1;
|
||||
public Dictionary<int, Cluster> thingClusters = new();
|
||||
|
||||
@ -93,6 +95,8 @@ namespace NanoBrain {
|
||||
nucleus.ShallowCloneTo(this);
|
||||
}
|
||||
Nucleus[] clonedNuclei = this.clusterNuclei.ToArray();
|
||||
// foreach (Nucleus n in clonedNuclei)
|
||||
// n.name += "(c)";
|
||||
|
||||
// Now clone the connections
|
||||
for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) {
|
||||
@ -106,19 +110,15 @@ namespace NanoBrain {
|
||||
|
||||
foreach (Synapse prefabSynapse in prefabNeuron.synapses) {
|
||||
Neuron synapseNeuron = prefabSynapse.neuron;
|
||||
if (synapseNeuron.parent is not null && synapseNeuron.clusterPrefab != this.clusterPrefab) {
|
||||
if (synapseNeuron.clusterPrefab != null && synapseNeuron.clusterPrefab != this.prefab) {
|
||||
// Neuron is in another cluster, find the cloned cluster first
|
||||
Cluster prefabCluster = synapseNeuron.parent;
|
||||
int clusterIx = GetNucleusIndex(prefabNuclei, prefabCluster);
|
||||
if (clusterIx < 0)
|
||||
// Could not find the cluster in the prefab
|
||||
continue;
|
||||
if (clonedNuclei[clusterIx] is not Cluster clonedCluster)
|
||||
// Could not find the cloned cluster
|
||||
ClusterPrefab prefabCluster = synapseNeuron.clusterPrefab;
|
||||
Cluster clonedCluster = this.clusterNuclei.Find(n => n.name == prefabCluster.name) as Cluster;
|
||||
if (clonedCluster == null)
|
||||
continue;
|
||||
|
||||
// Now find the neuron in that cloned cluster
|
||||
int neuronIx = GetNucleusIndex(prefabCluster.prefab.nuclei, prefabSynapse.neuron);
|
||||
int neuronIx = GetNucleusIndex(prefabCluster.nuclei, prefabSynapse.neuron.name);
|
||||
if (neuronIx < 0)
|
||||
// Could not find the neuron in the prefab cluster
|
||||
continue;
|
||||
@ -127,6 +127,7 @@ namespace NanoBrain {
|
||||
continue;
|
||||
|
||||
clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
|
||||
//Debug.Log($"Add synapse {clonedCluster.name}.{clonedSender.name} -> {clonedNeuron.name} [{clonedSender.receivers.Count}]");
|
||||
}
|
||||
else {
|
||||
int ix = GetNucleusIndex(prefabNuclei, prefabSynapse.neuron);
|
||||
@ -137,6 +138,7 @@ namespace NanoBrain {
|
||||
|
||||
// Copy the receivers which will also create the synapse
|
||||
clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
|
||||
// Debug.Log($"Add synapse {clonedSender.name} -> {clonedNeuron.name}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,99 +164,157 @@ namespace NanoBrain {
|
||||
// clonedNeuron.AddReceiver(clonedReceiver, weight);
|
||||
// }
|
||||
}
|
||||
/*
|
||||
// Copy the siblings for clusters
|
||||
for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) {
|
||||
Nucleus prefabNucleus = prefabNuclei[nucleusIx];
|
||||
if (prefabNucleus is not Cluster prefabCluster)
|
||||
continue;
|
||||
|
||||
if (prefabCluster.siblingClusters == null || prefabCluster.siblingClusters.Length == 0)
|
||||
continue;
|
||||
if (Application.isPlaying) {
|
||||
// Only create cluster siblings at runtime
|
||||
foreach (Nucleus clonedNucleus in clonedNuclei) {
|
||||
if (clonedNucleus is not Cluster clonedCluster)
|
||||
continue;
|
||||
|
||||
Cluster clonedNucleus = clonedNuclei[nucleusIx] as Cluster;
|
||||
if (prefabCluster == prefabCluster.siblingClusters[0]) {
|
||||
// We clone the array only for the first entry
|
||||
//NucleusArray clonedArray = new(prefabReceptor.nucleiArray.Length);
|
||||
Cluster[] clonedArray = new Cluster[prefabCluster.siblingClusters.Length];
|
||||
int arrayIx = 0;
|
||||
foreach (Cluster prefabArrayNucleus in prefabCluster.siblingClusters) {
|
||||
int arrayNucleusIx = GetNucleusIndex(prefabNuclei, prefabArrayNucleus);
|
||||
if (arrayNucleusIx >= 0) {
|
||||
Cluster clonedArrayNucleus = clonedNuclei[arrayNucleusIx] as Cluster;
|
||||
clonedArray[arrayIx] = clonedArrayNucleus;
|
||||
}
|
||||
else {
|
||||
Debug.LogError($" Could not find prefab nucleus {prefabNucleus.name} in the clones");
|
||||
}
|
||||
arrayIx++;
|
||||
}
|
||||
clonedNucleus.siblingClusters = clonedArray;
|
||||
}
|
||||
else {
|
||||
// The others will refer to the array created for the first nucleus in the array
|
||||
int firstNucleusIx = GetNucleusIndex(prefabNuclei, prefabCluster.siblingClusters[0]);
|
||||
Cluster clonedFirstNucleus = clonedNuclei[firstNucleusIx] as Cluster;
|
||||
clonedNucleus.siblingClusters = clonedFirstNucleus.siblingClusters;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Collect the subclusters
|
||||
List<Cluster> subClusters = new();
|
||||
foreach (Nucleus nucleus in prefabNuclei) {
|
||||
foreach (Synapse synapse in nucleus.synapses) {
|
||||
Nucleus synapseNucleus = synapse.neuron;
|
||||
Cluster subCluster = synapseNucleus.parent;
|
||||
if (subCluster is null ||
|
||||
synapseNucleus.clusterPrefab == this.clusterPrefab) {
|
||||
|
||||
continue;
|
||||
}
|
||||
// if (synapseNucleus is not Cluster subCluster)
|
||||
// continue;
|
||||
if (subClusters.Contains(subCluster))
|
||||
continue;
|
||||
subClusters.Add(subCluster);
|
||||
}
|
||||
}
|
||||
// Create the subcluster instances
|
||||
foreach (Cluster subCluster in subClusters) {
|
||||
for (int ix = 0; ix < subCluster.instanceCount; ix++) {
|
||||
// create the new instance
|
||||
Cluster clusterInstance = new(subCluster.prefab);
|
||||
// connect it
|
||||
foreach ((Neuron sender, Nucleus receiver) in subCluster.CollectConnections()) {
|
||||
int receiverIx = GetNucleusIndex(prefabNuclei, receiver);
|
||||
if (receiverIx < 0)
|
||||
continue;
|
||||
|
||||
if (clonedNuclei[receiverIx] is not Nucleus clonedReceiver)
|
||||
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 == sender) {
|
||||
weight = synapse.weight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (clusterInstance.GetNucleus(sender.name) is not Neuron clonedSender)
|
||||
continue;
|
||||
|
||||
clonedSender.AddReceiver(clonedReceiver, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
foreach (Nucleus nucleus in this.clusterNuclei) {
|
||||
if (nucleus is Cluster clonedSubCluster)
|
||||
RestoreAllExternalReceivers(clonedSubCluster, this.prefab, this);
|
||||
List<Cluster> siblings = new() {
|
||||
clonedCluster
|
||||
};
|
||||
for (int instanceIx = 1; instanceIx < clonedCluster.instanceCount; instanceIx++) {
|
||||
// Create another sibling
|
||||
Debug.Log($"create {clonedCluster.prefab.name} sibling");
|
||||
Cluster sibling = new(clonedCluster.prefab, this) {
|
||||
name = $"{clonedCluster.baseName}: {instanceIx}",
|
||||
clusterPrefab = this.clusterPrefab,
|
||||
instanceCount = this.instanceCount,
|
||||
};
|
||||
siblings.Add(sibling);
|
||||
CopyAllExternalReceivers(clonedCluster, sibling, clonedCluster.prefab, this);
|
||||
}
|
||||
Cluster[] siblingClusters = siblings.ToArray();
|
||||
foreach (Cluster sibling in siblings)
|
||||
sibling.siblingClusters = siblingClusters;
|
||||
}
|
||||
}
|
||||
/*
|
||||
for (int nucleusIx = 0; nucleusIx < clonedNuclei.Length; nucleusIx++) {
|
||||
Nucleus prefabNucleus = prefabNuclei[nucleusIx];
|
||||
if (prefabNucleus is not Cluster prefabCluster)
|
||||
continue;
|
||||
|
||||
if (prefabCluster.instanceCount <= 1)
|
||||
continue;
|
||||
|
||||
Cluster clonedNucleus = clonedNuclei[nucleusIx] as Cluster;
|
||||
if (prefabCluster == prefabCluster.siblingClusters[0]) {
|
||||
// We clone the array only for the first entry
|
||||
//NucleusArray clonedArray = new(prefabReceptor.nucleiArray.Length);
|
||||
Cluster[] clonedArray = new Cluster[prefabCluster.siblingClusters.Length];
|
||||
int arrayIx = 0;
|
||||
foreach (Cluster prefabArrayNucleus in prefabCluster.siblingClusters) {
|
||||
int arrayNucleusIx = GetNucleusIndex(prefabNuclei, prefabArrayNucleus);
|
||||
if (arrayNucleusIx >= 0) {
|
||||
Cluster clonedArrayNucleus = clonedNuclei[arrayNucleusIx] as Cluster;
|
||||
clonedArray[arrayIx] = clonedArrayNucleus;
|
||||
}
|
||||
else {
|
||||
Debug.LogError($" Could not find prefab nucleus {prefabNucleus.name} in the clones");
|
||||
}
|
||||
arrayIx++;
|
||||
}
|
||||
clonedNucleus.siblingClusters = clonedArray;
|
||||
}
|
||||
else {
|
||||
// The others will refer to the array created for the first nucleus in the array
|
||||
int firstNucleusIx = GetNucleusIndex(prefabNuclei, prefabCluster.siblingClusters[0]);
|
||||
Cluster clonedFirstNucleus = clonedNuclei[firstNucleusIx] as Cluster;
|
||||
clonedNucleus.siblingClusters = clonedFirstNucleus.siblingClusters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Collect the subclusters
|
||||
List<Cluster> subClusters = new();
|
||||
foreach (Nucleus nucleus in prefabNuclei) {
|
||||
foreach (Synapse synapse in nucleus.synapses) {
|
||||
Nucleus synapseNucleus = synapse.neuron;
|
||||
Cluster subCluster = synapseNucleus.parent;
|
||||
if (subCluster is null ||
|
||||
synapseNucleus.clusterPrefab == this.clusterPrefab) {
|
||||
|
||||
continue;
|
||||
}
|
||||
// if (synapseNucleus is not Cluster subCluster)
|
||||
// continue;
|
||||
if (subClusters.Contains(subCluster))
|
||||
continue;
|
||||
subClusters.Add(subCluster);
|
||||
}
|
||||
}
|
||||
// Create the subcluster instances
|
||||
foreach (Cluster subCluster in subClusters) {
|
||||
for (int ix = 0; ix < subCluster.instanceCount; ix++) {
|
||||
// create the new instance
|
||||
Cluster clusterInstance = new(subCluster.prefab);
|
||||
// connect it
|
||||
foreach ((Neuron sender, Nucleus receiver) in subCluster.CollectConnections()) {
|
||||
int receiverIx = GetNucleusIndex(prefabNuclei, receiver);
|
||||
if (receiverIx < 0)
|
||||
continue;
|
||||
|
||||
if (clonedNuclei[receiverIx] is not Nucleus clonedReceiver)
|
||||
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 == sender) {
|
||||
weight = synapse.weight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (clusterInstance.GetNucleus(sender.name) is not Neuron clonedSender)
|
||||
continue;
|
||||
|
||||
clonedSender.AddReceiver(clonedReceiver, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// foreach (Nucleus nucleus in this.clusterNuclei) {
|
||||
// if (nucleus is Cluster clonedSubCluster)
|
||||
// RestoreAllExternalReceivers(clonedSubCluster, this.prefab, this);
|
||||
// }
|
||||
}
|
||||
|
||||
private void CloneSynapses(Neuron prefabNeuron, Neuron clonedNeuron) {
|
||||
foreach (Synapse prefabSynapse in prefabNeuron.synapses) {
|
||||
Neuron synapseNeuron = prefabSynapse.neuron;
|
||||
if (synapseNeuron.clusterPrefab != null && synapseNeuron.clusterPrefab != this.prefab) {
|
||||
// Neuron is in another cluster, find the cloned cluster first
|
||||
ClusterPrefab prefabCluster = synapseNeuron.clusterPrefab;
|
||||
Cluster clonedCluster = this.clusterNuclei.Find(n => n.name == prefabCluster.name) as Cluster;
|
||||
if (clonedCluster == null)
|
||||
continue;
|
||||
|
||||
// Now find the neuron in that cloned cluster
|
||||
int neuronIx = GetNucleusIndex(prefabCluster.nuclei, prefabSynapse.neuron.name);
|
||||
if (neuronIx < 0)
|
||||
// Could not find the neuron in the prefab cluster
|
||||
continue;
|
||||
if (clonedCluster.clusterNuclei[neuronIx] is not Neuron clonedSender)
|
||||
// Could not find the neuron in the cloned cluster
|
||||
continue;
|
||||
|
||||
clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
|
||||
//Debug.Log($"Add synapse {clonedCluster.name}.{clonedSender.name} -> {clonedNeuron.name} [{clonedSender.receivers.Count}]");
|
||||
}
|
||||
else {
|
||||
Neuron clonedSender = this.clusterNuclei.Find(n => n.name == prefabSynapse.neuron.name) as Neuron;
|
||||
// Copy the receivers which will also create the synapse
|
||||
clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
|
||||
// Debug.Log($"Add synapse {clonedSender.name} -> {clonedNeuron.name}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -347,33 +407,29 @@ namespace NanoBrain {
|
||||
Cluster clone = new(this.prefab, parent) {
|
||||
name = this.name,
|
||||
clusterPrefab = this.clusterPrefab,
|
||||
instanceCount = this.instanceCount,
|
||||
};
|
||||
// Somehow siblingClusters should be cloned too. Believe I do this in ClonePrefab right now.
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static void RestoreAllExternalReceivers(Cluster clonedCluster, ClusterPrefab prefabParent, Cluster clonedParent) {
|
||||
int clonedClusterIx = GetNucleusIndex(clonedParent.clusterNuclei, clonedCluster);
|
||||
if (prefabParent.nuclei[clonedClusterIx] is not Cluster sourceCluster)
|
||||
return;
|
||||
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;
|
||||
|
||||
if (clonedCluster.clusterNuclei[nucleusIx] is not Neuron clonedNeuron)
|
||||
if (sibling.clusterNuclei[nucleusIx] is not Neuron clonedNeuron)
|
||||
continue;
|
||||
|
||||
// copy the receivers (and thus synapses) from the source to the clone
|
||||
// copy the receivers (and thus synapses) from the source to the sibling
|
||||
foreach (Nucleus receiver in sourceNeuron.receivers) {
|
||||
int ix = GetNucleusIndex(prefabParent.nuclei, receiver);
|
||||
int ix = GetNucleusIndex(clonedParent.clusterNuclei, 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) {
|
||||
@ -384,10 +440,11 @@ namespace NanoBrain {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -402,13 +459,25 @@ namespace NanoBrain {
|
||||
int i = 0;
|
||||
foreach (Nucleus nucleiElement in nuclei) {
|
||||
//for (int i = 0; i < nuclei.Length; i++) {
|
||||
if (nucleus == nucleiElement)
|
||||
if (nucleiElement == nucleus)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int GetNucleusIndex(List<Nucleus> nuclei, string nucleusName) {
|
||||
int i = 0;
|
||||
foreach (Nucleus nucleiElement in nuclei) {
|
||||
//for (int i = 0; i < nuclei.Length; i++) {
|
||||
if (nucleiElement.name == nucleusName)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#endregion Init
|
||||
|
||||
#region Cluster Array
|
||||
@ -470,6 +539,7 @@ namespace NanoBrain {
|
||||
return cluster;
|
||||
|
||||
Cluster selectedCluster = SelectCluster();
|
||||
selectedCluster.name = baseName + ": " + thingName;
|
||||
thingClusters[thingId] = selectedCluster;
|
||||
return selectedCluster;
|
||||
}
|
||||
@ -528,10 +598,6 @@ namespace NanoBrain {
|
||||
|
||||
#endregion ClusterArray
|
||||
|
||||
|
||||
|
||||
//public Dictionary<string, Nucleus> nucleiDict = new();
|
||||
|
||||
public List<Nucleus> _inputs = null;
|
||||
public virtual List<Nucleus> inputs {
|
||||
get {
|
||||
@ -550,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();
|
||||
@ -575,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
|
||||
@ -604,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;
|
||||
}
|
||||
@ -643,6 +716,9 @@ namespace NanoBrain {
|
||||
return this._outputs;
|
||||
}
|
||||
}
|
||||
public void RefreshOutputs() {
|
||||
this._outputs = null;
|
||||
}
|
||||
|
||||
public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) {
|
||||
foreach (Nucleus receptor in this.clusterNuclei) {
|
||||
@ -685,18 +761,39 @@ namespace NanoBrain {
|
||||
}
|
||||
}
|
||||
|
||||
public bool DeleteNucleus(Nucleus nucleus) {
|
||||
if (this.clusterNuclei.Contains(nucleus) == false) {
|
||||
// Try to find the nucleus by name
|
||||
if (TryGetNucleus(nucleus.name, out nucleus) == false)
|
||||
return false;
|
||||
}
|
||||
|
||||
Neuron.Delete(nucleus);
|
||||
int nucleusIx = this.clusterNuclei.IndexOf(nucleus);
|
||||
this.clusterNuclei.Remove(nucleus);
|
||||
this.prefab.nuclei.RemoveAt(nucleusIx);
|
||||
RefreshOutputs();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Receivers
|
||||
|
||||
public virtual List<Nucleus> CollectReceivers() {
|
||||
public virtual List<Nucleus> CollectReceivers(bool removeDuplicates = false) {
|
||||
List<Nucleus> receivers = new();
|
||||
foreach (Nucleus outputNucleus in this.clusterNuclei) {
|
||||
if (outputNucleus is not Neuron output)
|
||||
continue;
|
||||
|
||||
// Debug.Log($"output {this.name} {outputNucleus.name}");
|
||||
foreach (Nucleus receiver in output.receivers) {
|
||||
// Only add receivers outside this cluster
|
||||
if (receiver.clusterPrefab != this.prefab)
|
||||
receivers.Add(receiver);
|
||||
// Debug.Log($"output {receiver.name}");
|
||||
// Only add receivers outside this cluster
|
||||
if (receiver.clusterPrefab != this.prefab) {
|
||||
if (removeDuplicates == false || receivers.Contains(receiver) == false)
|
||||
// Debug.Log($" YES");
|
||||
receivers.Add(receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
return receivers;
|
||||
@ -717,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}");
|
||||
@ -752,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();
|
||||
}
|
||||
|
||||
@ -33,8 +33,10 @@ namespace NanoBrain {
|
||||
public Neuron(ClusterPrefab prefab, string name) {
|
||||
this.clusterPrefab = prefab;
|
||||
this.name = name;
|
||||
if (this.clusterPrefab != null)
|
||||
if (this.clusterPrefab != null) {
|
||||
this.clusterPrefab.nuclei.Add(this);
|
||||
this.clusterPrefab.RefreshOutputs();
|
||||
}
|
||||
else
|
||||
Debug.LogError("No prefab when adding neuron to prefab");
|
||||
}
|
||||
@ -264,15 +266,19 @@ namespace NanoBrain {
|
||||
}
|
||||
|
||||
public static void Delete(Nucleus nucleus) {
|
||||
foreach (Synapse synapse in nucleus.synapses) {
|
||||
if (synapse.neuron is Neuron synapse_nucleus) {
|
||||
if (synapse_nucleus.receivers.Count > 1) {
|
||||
// there is another nucleus feeding into this input nucleus
|
||||
synapse_nucleus.receivers.RemoveAll(r => r == nucleus);
|
||||
}
|
||||
else {
|
||||
// No other links, delete it.
|
||||
Neuron.Delete(synapse_nucleus);
|
||||
if (nucleus == null)
|
||||
return;
|
||||
if (nucleus.synapses != null) {
|
||||
foreach (Synapse synapse in nucleus.synapses) {
|
||||
if (synapse.neuron is Neuron synapse_nucleus) {
|
||||
if (synapse_nucleus.receivers.Count > 1) {
|
||||
// there is another nucleus feeding into this input nucleus
|
||||
synapse_nucleus.receivers.RemoveAll(r => r == nucleus);
|
||||
}
|
||||
else {
|
||||
// No other links, delete it.
|
||||
Neuron.Delete(synapse_nucleus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -520,6 +526,8 @@ namespace NanoBrain {
|
||||
public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) {
|
||||
this._receivers.Add(receiverToAdd);
|
||||
receiverToAdd.AddSynapse(this, weight);
|
||||
//Debug.Log($"Add synapse {this.clusterPrefab.name}.{this.name} -> {receiverToAdd.name} --- [{this.receivers.Count}]");
|
||||
|
||||
}
|
||||
|
||||
public virtual void RemoveReceiver(Nucleus receiverToRemove) {
|
||||
|
||||
@ -29,11 +29,7 @@ namespace NanoBrain {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
public bool isSleeping {
|
||||
get {
|
||||
return this.neuron.isSleeping;
|
||||
}
|
||||
}
|
||||
public bool isSleeping => this.neuron.isSleeping;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user