Compare commits
No commits in common. "7ef8e42e091cd5a46bf77dfbf9b1a3b3a4422752" and "cc9a845b643ffb4a9abe4f7da787ac5c5b14dae8" have entirely different histories.
7ef8e42e09
...
cc9a845b64
@ -64,22 +64,30 @@ namespace NanoBrain {
|
|||||||
public class GraphEditor : GraphView {
|
public class GraphEditor : GraphView {
|
||||||
|
|
||||||
protected ClusterPrefab prefab;
|
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) {
|
public GraphEditor(ClusterPrefab prefab) : base(prefab.output.parent) {
|
||||||
this.prefab = prefab;
|
this.prefab = prefab;
|
||||||
|
|
||||||
// In a Prefab editor, no instance exists but we need it for the ClusterViewer.
|
// In a Prefab editor, no instance exists but we need it for the ClusterViewer.
|
||||||
// So we create a temporary instance
|
// So we create a temporary instance
|
||||||
this.currentCluster = new(prefab);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetGraph(GameObject gameObject, VisualElement inspectorContainer) {
|
public void SetGraph(GameObject gameObject, VisualElement inspectorContainer) {
|
||||||
@ -143,47 +151,41 @@ namespace NanoBrain {
|
|||||||
if (serializedObject == null || serializedObject.targetObject == null)
|
if (serializedObject == null || serializedObject.targetObject == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (this.currentNucleus == null)
|
||||||
|
return;
|
||||||
|
|
||||||
serializedObject.Update();
|
serializedObject.Update();
|
||||||
|
|
||||||
GUIStyle boldTextFieldStyle = new(EditorStyles.textField) {
|
|
||||||
fontStyle = FontStyle.Bold
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.currentNucleus == null) {
|
|
||||||
OutputsInspector(ref anythingChanged);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
|
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
|
||||||
alignment = TextAnchor.MiddleLeft,
|
alignment = TextAnchor.MiddleLeft,
|
||||||
margin = new RectOffset(10, 0, 4, 4)
|
margin = new RectOffset(10, 0, 4, 4)
|
||||||
};
|
};
|
||||||
|
GUIStyle boldTextFieldStyle = new(EditorStyles.textField) {
|
||||||
|
fontStyle = FontStyle.Bold
|
||||||
|
};
|
||||||
|
|
||||||
// Nucleus type
|
// Nucleus type
|
||||||
string nucleusType = this.currentNucleus.GetType().Name;
|
string nucleusType = this.currentNucleus.GetType().Name;
|
||||||
GUILayout.Label(nucleusType, headerStyle);
|
GUILayout.Label(nucleusType, headerStyle);
|
||||||
|
|
||||||
// Nucleus name
|
// Nucleus name
|
||||||
Cluster cluster = this.currentPrefabNucleus as Cluster;
|
if (this.currentNucleus.parent is Cluster parentCluster) {
|
||||||
if (cluster != null) {
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.BeginHorizontal();
|
||||||
if (GUILayout.Button(this.currentNucleus.parent.name))
|
if (GUILayout.Button(this.currentNucleus.parent.name))
|
||||||
OnClusterClick(cluster);
|
OnClusterClick(parentCluster);
|
||||||
EditorGUI.BeginDisabledGroup(true);
|
EditorGUI.BeginDisabledGroup(true);
|
||||||
EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
||||||
EditorGUI.EndDisabledGroup();
|
EditorGUI.EndDisabledGroup();
|
||||||
if (GUILayout.Button("Reimport"))
|
if (GUILayout.Button("Reimport"))
|
||||||
ReimportCluster(cluster);
|
ReimportCluster(parentCluster);
|
||||||
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
||||||
if (newName != this.currentNucleus.name) {
|
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.currentNucleus.name = newName;
|
||||||
this.prefab.RefreshOutputs();
|
this.prefab.RefreshOutputs();
|
||||||
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
||||||
anythingChanged = true;
|
anythingChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +206,7 @@ namespace NanoBrain {
|
|||||||
if (this.currentNucleus is MemoryCell memory)
|
if (this.currentNucleus is MemoryCell memory)
|
||||||
MemoryCellInspector(memory, ref anythingChanged);
|
MemoryCellInspector(memory, ref anythingChanged);
|
||||||
// Cluster
|
// Cluster
|
||||||
else if (cluster != null)
|
else if (this.currentNucleus is Cluster cluster)
|
||||||
ClusterInspector(cluster, ref anythingChanged);
|
ClusterInspector(cluster, ref anythingChanged);
|
||||||
// Other
|
// Other
|
||||||
else
|
else
|
||||||
@ -212,7 +214,6 @@ namespace NanoBrain {
|
|||||||
|
|
||||||
if (GUILayout.Button("Delete"))
|
if (GUILayout.Button("Delete"))
|
||||||
DeleteNucleus(this.currentNucleus);
|
DeleteNucleus(this.currentNucleus);
|
||||||
}
|
|
||||||
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
if (anythingChanged) {
|
if (anythingChanged) {
|
||||||
@ -221,24 +222,6 @@ 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) {
|
protected void MemoryCellInspector(MemoryCell memoryCell, ref bool anythingChanged) {
|
||||||
memoryCell.staticMemory = EditorGUILayout.Toggle("Static Memory", memoryCell.staticMemory);
|
memoryCell.staticMemory = EditorGUILayout.Toggle("Static Memory", memoryCell.staticMemory);
|
||||||
NucleusInspector(memoryCell, ref anythingChanged);
|
NucleusInspector(memoryCell, ref anythingChanged);
|
||||||
@ -258,6 +241,7 @@ namespace NanoBrain {
|
|||||||
|
|
||||||
if (GUILayout.Button("Add")) {
|
if (GUILayout.Button("Add")) {
|
||||||
Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name);
|
Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name);
|
||||||
|
//cluster.AddInstance(this.prefab);
|
||||||
cluster.AddInstance();
|
cluster.AddInstance();
|
||||||
anythingChanged = true;
|
anythingChanged = true;
|
||||||
}
|
}
|
||||||
@ -300,17 +284,14 @@ namespace NanoBrain {
|
|||||||
EditorGUIUtility.labelWidth = 100;
|
EditorGUIUtility.labelWidth = 100;
|
||||||
|
|
||||||
Vector3 newBias = EditorGUILayout.Vector3Field("Bias", this.currentNucleus.bias);
|
Vector3 newBias = EditorGUILayout.Vector3Field("Bias", this.currentNucleus.bias);
|
||||||
if (newBias != this.currentPrefabNucleus.bias) {
|
|
||||||
anythingChanged |= newBias != this.currentNucleus.bias;
|
anythingChanged |= newBias != this.currentNucleus.bias;
|
||||||
this.currentPrefabNucleus.bias = newBias;
|
|
||||||
this.currentNucleus.bias = newBias;
|
this.currentNucleus.bias = newBias;
|
||||||
}
|
|
||||||
EditorGUIUtility.labelWidth = previousLabelWidth;
|
EditorGUIUtility.labelWidth = previousLabelWidth;
|
||||||
|
|
||||||
Nucleus[] array = null;
|
Nucleus[] array = null;
|
||||||
int elementIx = -1;
|
int elementIx = -1;
|
||||||
if (this.currentPrefabNucleus.synapses.Count > 0) {
|
if (this.currentNucleus.synapses.Count > 0) {
|
||||||
Synapse[] synapses = this.currentPrefabNucleus.synapses.ToArray();
|
Synapse[] synapses = this.currentNucleus.synapses.ToArray();
|
||||||
foreach (Synapse synapse in synapses) {
|
foreach (Synapse synapse in synapses) {
|
||||||
if (synapse.neuron == null)
|
if (synapse.neuron == null)
|
||||||
continue;
|
continue;
|
||||||
@ -349,20 +330,24 @@ namespace NanoBrain {
|
|||||||
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
if (synapse.neuron.clusterPrefab != this.currentNucleus.clusterPrefab) {
|
if (synapse.neuron.clusterPrefab != this.currentNucleus.clusterPrefab) {
|
||||||
// If it is a different cluster
|
// If it is a cluster
|
||||||
GUIStyle labelStyle = new(GUI.skin.label);
|
GUIStyle labelStyle = new(GUI.skin.label);
|
||||||
float labelWidth = 200;
|
float labelWidth = 200;
|
||||||
if (synapse.neuron.clusterPrefab != null) {
|
if (synapse.neuron.clusterPrefab != null) {
|
||||||
labelWidth = labelStyle.CalcSize(new GUIContent($"{synapse.neuron.clusterPrefab.name}.")).x;
|
labelWidth = labelStyle.CalcSize(new GUIContent($"{synapse.neuron.clusterPrefab.name}.")).x;
|
||||||
GUILayout.Label($"{synapse.neuron.clusterPrefab.name}", GUILayout.Width(labelWidth));
|
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();
|
string[] options = synapse.neuron.clusterPrefab.nuclei.Select(n => n.name).ToArray();
|
||||||
int selectedIndex = System.Array.IndexOf(options, synapse.neuron.name);
|
int selectedIndex = System.Array.IndexOf(options, synapse.neuron.name);
|
||||||
int newIndex = EditorGUILayout.Popup(selectedIndex, options);
|
int newIndex = EditorGUILayout.Popup(selectedIndex, options);
|
||||||
|
// if (newIndex != selectedIndex && synapse.neuron.clusterPrefab.nuclei[newIndex] is Neuron newNeuron)
|
||||||
|
// ChangeSynapse(synapse, newNeuron);
|
||||||
if (newIndex != selectedIndex) {
|
if (newIndex != selectedIndex) {
|
||||||
// Nucleus selectedNucleus = synapse.neuron.parent.clusterNuclei[newIndex];
|
// It shall be ensured that the parent.clusterNuclei and
|
||||||
// Neuron newNeuron = selectedNucleus as Neuron;
|
// clusterPrefab.nuclei contain the same neurons in the same order....
|
||||||
Neuron newNeuron = synapse.neuron.clusterPrefab.nuclei[newIndex] as Neuron;
|
Nucleus selectedNucleus = synapse.neuron.parent.clusterNuclei[newIndex];
|
||||||
|
Neuron newNeuron = selectedNucleus as Neuron;
|
||||||
ChangeSynapse(synapse, newNeuron);
|
ChangeSynapse(synapse, newNeuron);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -382,6 +367,14 @@ namespace NanoBrain {
|
|||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
float newWeight = EditorGUILayout.FloatField("Weight", synapse.weight);
|
float newWeight = EditorGUILayout.FloatField("Weight", synapse.weight);
|
||||||
if (newWeight != 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;
|
synapse.weight = newWeight;
|
||||||
anythingChanged = true;
|
anythingChanged = true;
|
||||||
}
|
}
|
||||||
@ -437,6 +430,15 @@ namespace NanoBrain {
|
|||||||
case Nucleus.Type.Cluster:
|
case Nucleus.Type.Cluster:
|
||||||
AddClusterInput(nucleus);
|
AddClusterInput(nucleus);
|
||||||
break;
|
break;
|
||||||
|
// case Nucleus.Type.Receptor:
|
||||||
|
// AddReceptorInput(nucleus);
|
||||||
|
// break;
|
||||||
|
// case Nucleus.Type.ClusterReceptor:
|
||||||
|
// AddClusterReceptorInput(nucleus);
|
||||||
|
// break;
|
||||||
|
// case Nucleus.Type.ClusterArray:
|
||||||
|
// AddClusterArrayInput(nucleus);
|
||||||
|
// break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -533,15 +535,17 @@ namespace NanoBrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.currentCluster.DeleteNucleus(nucleus);//clusterNuclei.Remove(nucleus);
|
this.prefab.nuclei.Remove(nucleus);
|
||||||
|
|
||||||
// this.prefab.nuclei.Remove(nucleus);
|
if (outputsPopup.value == nucleus.name) {
|
||||||
// Neuron.Delete(nucleus);
|
|
||||||
this.prefab.RefreshOutputs();
|
this.prefab.RefreshOutputs();
|
||||||
|
outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
||||||
|
outputsPopup.index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Neuron.Delete(nucleus);
|
||||||
|
|
||||||
this.currentNucleus = this.prefab.output;
|
this.currentNucleus = this.prefab.output;
|
||||||
this.selectedOutput = this.currentNucleus;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Nucleus.Type selectedType = Nucleus.Type.None;
|
Nucleus.Type selectedType = Nucleus.Type.None;
|
||||||
@ -566,7 +570,7 @@ namespace NanoBrain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void ChangeSynapse(Synapse synapse, Neuron newNucleus) {
|
protected virtual void ChangeSynapse(Synapse synapse, Neuron newNucleus) {
|
||||||
Neuron synapseNeuron = synapse.neuron;
|
Neuron synapseNeuron = synapse.neuron as Neuron;
|
||||||
if (synapse.neuron.parent is Cluster subCluster && subCluster.prefab != this.prefab) {
|
if (synapse.neuron.parent is Cluster subCluster && subCluster.prefab != this.prefab) {
|
||||||
// if (synapse.neuron.parent is ClusterReceptor receptor) {
|
// if (synapse.neuron.parent is ClusterReceptor receptor) {
|
||||||
// // the new nucleus is part of a (cluster) receptor,
|
// // the new nucleus is part of a (cluster) receptor,
|
||||||
@ -596,8 +600,8 @@ namespace NanoBrain {
|
|||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// it is a neuron in a subcluster
|
// it is a neuron in a subcluster
|
||||||
synapseNeuron.RemoveReceiver(this.currentPrefabNucleus);
|
synapseNeuron.RemoveReceiver(this.currentNucleus);
|
||||||
newNucleus.AddReceiver(this.currentPrefabNucleus);
|
newNucleus.AddReceiver(this.currentNucleus);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -15,15 +15,8 @@ namespace NanoBrain {
|
|||||||
//protected readonly ClusterPrefab prefab;
|
//protected readonly ClusterPrefab prefab;
|
||||||
protected Cluster currentCluster;
|
protected Cluster currentCluster;
|
||||||
protected SerializedObject serializedBrain;
|
protected SerializedObject serializedBrain;
|
||||||
protected Nucleus _currentNucleus;
|
protected Nucleus currentNucleus;
|
||||||
protected virtual Nucleus currentNucleus {
|
|
||||||
get => _currentNucleus;
|
|
||||||
set => _currentNucleus = value;
|
|
||||||
}
|
|
||||||
//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;
|
||||||
@ -32,7 +25,7 @@ namespace NanoBrain {
|
|||||||
protected VisualElement topMenuContainer;
|
protected VisualElement topMenuContainer;
|
||||||
protected ScrollView scrollView;
|
protected ScrollView scrollView;
|
||||||
protected IMGUIContainer graphContainer;
|
protected IMGUIContainer graphContainer;
|
||||||
//protected readonly PopupField<string> outputsPopup;
|
protected readonly PopupField<string> outputsPopup;
|
||||||
|
|
||||||
public enum Mode {
|
public enum Mode {
|
||||||
Focus,
|
Focus,
|
||||||
@ -329,7 +322,7 @@ namespace NanoBrain {
|
|||||||
if (nucleus is Neuron neuron)
|
if (nucleus is Neuron neuron)
|
||||||
receivers = neuron.receivers;
|
receivers = neuron.receivers;
|
||||||
else if (nucleus is Cluster cluster)
|
else if (nucleus is Cluster cluster)
|
||||||
receivers = cluster.CollectReceivers(true);
|
receivers = cluster.CollectReceivers();
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -359,6 +352,7 @@ namespace NanoBrain {
|
|||||||
float margin = 10 + spacing / 2;
|
float margin = 10 + spacing / 2;
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
List<Nucleus[]> drawnArrays = new();
|
||||||
foreach (Nucleus receiver in receivers) {
|
foreach (Nucleus receiver in receivers) {
|
||||||
Nucleus receiverNucleus = receiver;
|
Nucleus receiverNucleus = receiver;
|
||||||
if (receiverNucleus == null)
|
if (receiverNucleus == null)
|
||||||
@ -384,10 +378,6 @@ 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;
|
||||||
|
|
||||||
@ -395,19 +385,15 @@ 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<string> drawnNeuronNames = new();
|
List<Neuron> drawnNeurons = 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
|
||||||
string neuronName = synapse.neuron.name;
|
if (drawnNeurons.Contains(synapse.neuron))
|
||||||
if (synapse.neuron.parent != null)
|
|
||||||
neuronName = synapse.neuron.parent.baseName + "." + neuronName;
|
|
||||||
|
|
||||||
if (drawnNeuronNames.Contains(neuronName))
|
|
||||||
continue;
|
continue;
|
||||||
drawnNeuronNames.Add(neuronName);
|
drawnNeurons.Add(synapse.neuron);
|
||||||
|
|
||||||
float value = synapse.neuron.outputMagnitude * synapse.weight;
|
float value = synapse.neuron.outputMagnitude * synapse.weight;
|
||||||
if (value > maxValue)
|
if (value > maxValue)
|
||||||
@ -421,20 +407,15 @@ namespace NanoBrain {
|
|||||||
float margin = 10 + spacing / 2;
|
float margin = 10 + spacing / 2;
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
//List<Neuron> drawnNeurons = new();
|
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
|
||||||
string neuronName = synapse.neuron.name;
|
if (drawnNeurons.Contains(synapse.neuron))
|
||||||
if (synapse.neuron.parent != null)
|
|
||||||
neuronName = synapse.neuron.parent.baseName + "." + neuronName;
|
|
||||||
|
|
||||||
if (drawnNeuronNames.Contains(neuronName))
|
|
||||||
continue;
|
continue;
|
||||||
drawnNeuronNames.Add(neuronName);
|
drawnNeurons.Add(synapse.neuron);
|
||||||
|
|
||||||
Vector3 pos = new(250, margin + row * spacing, 0.0f);
|
Vector3 pos = new(250, margin + row * spacing, 0.0f);
|
||||||
DrawEdge(parentPos, pos);
|
DrawEdge(parentPos, pos);
|
||||||
@ -452,50 +433,6 @@ 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) {
|
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
|
||||||
@ -595,7 +532,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;
|
||||||
@ -789,34 +726,25 @@ namespace NanoBrain {
|
|||||||
|
|
||||||
protected void OnNeuronClick(Nucleus nucleus) {
|
protected void OnNeuronClick(Nucleus nucleus) {
|
||||||
if (nucleus == this.currentNucleus) {
|
if (nucleus == this.currentNucleus) {
|
||||||
this.selectedSynapseNeuron = null;
|
if (Application.isPlaying) {
|
||||||
// if (Application.isPlaying) {
|
if (nucleus is Cluster)
|
||||||
// if (nucleus is Cluster)
|
expandArray = !expandArray;
|
||||||
// expandArray = !expandArray;
|
else
|
||||||
// else
|
expandArray = false;
|
||||||
// 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;
|
||||||
|
|||||||
@ -32,11 +32,9 @@ namespace NanoBrain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This should not be serialized
|
// This should not be serialized
|
||||||
//[SerializeReference]
|
[SerializeReference]
|
||||||
[NonSerialized]
|
|
||||||
public Cluster[] siblingClusters;
|
public Cluster[] siblingClusters;
|
||||||
// This serialization should be enough
|
// This serialization should be enough
|
||||||
[SerializeField]
|
|
||||||
public int instanceCount = 1;
|
public int instanceCount = 1;
|
||||||
public Dictionary<int, Cluster> thingClusters = new();
|
public Dictionary<int, Cluster> thingClusters = new();
|
||||||
|
|
||||||
@ -95,8 +93,6 @@ namespace NanoBrain {
|
|||||||
nucleus.ShallowCloneTo(this);
|
nucleus.ShallowCloneTo(this);
|
||||||
}
|
}
|
||||||
Nucleus[] clonedNuclei = this.clusterNuclei.ToArray();
|
Nucleus[] clonedNuclei = this.clusterNuclei.ToArray();
|
||||||
// foreach (Nucleus n in clonedNuclei)
|
|
||||||
// n.name += "(c)";
|
|
||||||
|
|
||||||
// Now clone the connections
|
// Now clone the connections
|
||||||
for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) {
|
for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) {
|
||||||
@ -110,15 +106,19 @@ namespace NanoBrain {
|
|||||||
|
|
||||||
foreach (Synapse prefabSynapse in prefabNeuron.synapses) {
|
foreach (Synapse prefabSynapse in prefabNeuron.synapses) {
|
||||||
Neuron synapseNeuron = prefabSynapse.neuron;
|
Neuron synapseNeuron = prefabSynapse.neuron;
|
||||||
if (synapseNeuron.clusterPrefab != null && synapseNeuron.clusterPrefab != this.prefab) {
|
if (synapseNeuron.parent is not null && synapseNeuron.clusterPrefab != this.clusterPrefab) {
|
||||||
// Neuron is in another cluster, find the cloned cluster first
|
// Neuron is in another cluster, find the cloned cluster first
|
||||||
ClusterPrefab prefabCluster = synapseNeuron.clusterPrefab;
|
Cluster prefabCluster = synapseNeuron.parent;
|
||||||
Cluster clonedCluster = this.clusterNuclei.Find(n => n.name == prefabCluster.name) as Cluster;
|
int clusterIx = GetNucleusIndex(prefabNuclei, prefabCluster);
|
||||||
if (clonedCluster == null)
|
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
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Now find the neuron in that cloned cluster
|
// Now find the neuron in that cloned cluster
|
||||||
int neuronIx = GetNucleusIndex(prefabCluster.nuclei, prefabSynapse.neuron.name);
|
int neuronIx = GetNucleusIndex(prefabCluster.prefab.nuclei, prefabSynapse.neuron);
|
||||||
if (neuronIx < 0)
|
if (neuronIx < 0)
|
||||||
// Could not find the neuron in the prefab cluster
|
// Could not find the neuron in the prefab cluster
|
||||||
continue;
|
continue;
|
||||||
@ -127,7 +127,6 @@ namespace NanoBrain {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
|
clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
|
||||||
//Debug.Log($"Add synapse {clonedCluster.name}.{clonedSender.name} -> {clonedNeuron.name} [{clonedSender.receivers.Count}]");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int ix = GetNucleusIndex(prefabNuclei, prefabSynapse.neuron);
|
int ix = GetNucleusIndex(prefabNuclei, prefabSynapse.neuron);
|
||||||
@ -138,7 +137,6 @@ namespace NanoBrain {
|
|||||||
|
|
||||||
// Copy the receivers which will also create the synapse
|
// Copy the receivers which will also create the synapse
|
||||||
clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
|
clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
|
||||||
// Debug.Log($"Add synapse {clonedSender.name} -> {clonedNeuron.name}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,39 +162,14 @@ namespace NanoBrain {
|
|||||||
// clonedNeuron.AddReceiver(clonedReceiver, weight);
|
// clonedNeuron.AddReceiver(clonedReceiver, weight);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Application.isPlaying) {
|
|
||||||
// Only create cluster siblings at runtime
|
|
||||||
foreach (Nucleus clonedNucleus in clonedNuclei) {
|
|
||||||
if (clonedNucleus is not Cluster clonedCluster)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
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++) {
|
// Copy the siblings for clusters
|
||||||
|
for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) {
|
||||||
Nucleus prefabNucleus = prefabNuclei[nucleusIx];
|
Nucleus prefabNucleus = prefabNuclei[nucleusIx];
|
||||||
if (prefabNucleus is not Cluster prefabCluster)
|
if (prefabNucleus is not Cluster prefabCluster)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (prefabCluster.instanceCount <= 1)
|
if (prefabCluster.siblingClusters == null || prefabCluster.siblingClusters.Length == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Cluster clonedNucleus = clonedNuclei[nucleusIx] as Cluster;
|
Cluster clonedNucleus = clonedNuclei[nucleusIx] as Cluster;
|
||||||
@ -225,7 +198,6 @@ namespace NanoBrain {
|
|||||||
clonedNucleus.siblingClusters = clonedFirstNucleus.siblingClusters;
|
clonedNucleus.siblingClusters = clonedFirstNucleus.siblingClusters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Collect the subclusters
|
// Collect the subclusters
|
||||||
@ -279,42 +251,10 @@ namespace NanoBrain {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// foreach (Nucleus nucleus in this.clusterNuclei) {
|
foreach (Nucleus nucleus in this.clusterNuclei) {
|
||||||
// if (nucleus is Cluster clonedSubCluster)
|
if (nucleus is Cluster clonedSubCluster)
|
||||||
// RestoreAllExternalReceivers(clonedSubCluster, this.prefab, this);
|
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>
|
/// <summary>
|
||||||
@ -407,29 +347,33 @@ namespace NanoBrain {
|
|||||||
Cluster clone = new(this.prefab, parent) {
|
Cluster clone = new(this.prefab, parent) {
|
||||||
name = this.name,
|
name = this.name,
|
||||||
clusterPrefab = this.clusterPrefab,
|
clusterPrefab = this.clusterPrefab,
|
||||||
instanceCount = this.instanceCount,
|
|
||||||
};
|
};
|
||||||
// Somehow siblingClusters should be cloned too. Believe I do this in ClonePrefab right now.
|
// Somehow siblingClusters should be cloned too. Believe I do this in ClonePrefab right now.
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CopyAllExternalReceivers(Cluster sourceCluster, Cluster sibling, ClusterPrefab prefabParent, Cluster clonedParent) {
|
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;
|
||||||
|
|
||||||
for (int nucleusIx = 0; nucleusIx < sourceCluster.clusterNuclei.Count; nucleusIx++) {
|
for (int nucleusIx = 0; nucleusIx < sourceCluster.clusterNuclei.Count; nucleusIx++) {
|
||||||
Nucleus sourceNucleus = sourceCluster.clusterNuclei[nucleusIx];
|
Nucleus sourceNucleus = sourceCluster.clusterNuclei[nucleusIx];
|
||||||
if (sourceNucleus is not Neuron sourceNeuron)
|
if (sourceNucleus is not Neuron sourceNeuron)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sibling.clusterNuclei[nucleusIx] is not Neuron clonedNeuron)
|
if (clonedCluster.clusterNuclei[nucleusIx] is not Neuron clonedNeuron)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// copy the receivers (and thus synapses) from the source to the sibling
|
// copy the receivers (and thus synapses) from the source to the clone
|
||||||
foreach (Nucleus receiver in sourceNeuron.receivers) {
|
foreach (Nucleus receiver in sourceNeuron.receivers) {
|
||||||
int ix = GetNucleusIndex(clonedParent.clusterNuclei, receiver);
|
int ix = GetNucleusIndex(prefabParent.nuclei, receiver);
|
||||||
if (ix < 0 || ix >= clonedParent.clusterNuclei.Count)
|
if (ix < 0 || ix >= clonedParent.clusterNuclei.Count)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
Nucleus clonedReceiver = clonedParent.clusterNuclei[ix];
|
||||||
|
|
||||||
// Find the synapse for the weight
|
// Find the synapse for the weight
|
||||||
float weight = 1;
|
float weight = 1;
|
||||||
foreach (Synapse synapse in receiver.synapses) {
|
foreach (Synapse synapse in receiver.synapses) {
|
||||||
@ -440,11 +384,10 @@ namespace NanoBrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clonedNeuron.AddReceiver(receiver, weight);
|
clonedNeuron.AddReceiver(clonedReceiver, weight);
|
||||||
Debug.Log($"external: {receiver.name} receives from {clonedNeuron.name} {clonedNeuron.GetHashCode()}");
|
// Debug.Log($"external: {clonedReceiver.name} receives from {clonedNeuron.name} {clonedNeuron.GetHashCode()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int GetNucleusIndex(Nucleus[] nuclei, Nucleus nucleus) {
|
protected int GetNucleusIndex(Nucleus[] nuclei, Nucleus nucleus) {
|
||||||
@ -459,25 +402,13 @@ namespace NanoBrain {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (Nucleus nucleiElement in nuclei) {
|
foreach (Nucleus nucleiElement in nuclei) {
|
||||||
//for (int i = 0; i < nuclei.Length; i++) {
|
//for (int i = 0; i < nuclei.Length; i++) {
|
||||||
if (nucleiElement == nucleus)
|
if (nucleus == nucleiElement)
|
||||||
return i;
|
return i;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return -1;
|
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
|
#endregion Init
|
||||||
|
|
||||||
#region Cluster Array
|
#region Cluster Array
|
||||||
@ -539,7 +470,6 @@ namespace NanoBrain {
|
|||||||
return cluster;
|
return cluster;
|
||||||
|
|
||||||
Cluster selectedCluster = SelectCluster();
|
Cluster selectedCluster = SelectCluster();
|
||||||
selectedCluster.name = baseName + ": " + thingName;
|
|
||||||
thingClusters[thingId] = selectedCluster;
|
thingClusters[thingId] = selectedCluster;
|
||||||
return selectedCluster;
|
return selectedCluster;
|
||||||
}
|
}
|
||||||
@ -598,6 +528,10 @@ namespace NanoBrain {
|
|||||||
|
|
||||||
#endregion ClusterArray
|
#endregion ClusterArray
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//public Dictionary<string, Nucleus> nucleiDict = new();
|
||||||
|
|
||||||
public List<Nucleus> _inputs = null;
|
public List<Nucleus> _inputs = null;
|
||||||
public virtual List<Nucleus> inputs {
|
public virtual List<Nucleus> inputs {
|
||||||
get {
|
get {
|
||||||
@ -616,33 +550,22 @@ namespace NanoBrain {
|
|||||||
|
|
||||||
public Dictionary<Nucleus, List<Nucleus>> computeOrders = new();
|
public Dictionary<Nucleus, List<Nucleus>> computeOrders = new();
|
||||||
private void ComputeOrders() {
|
private void ComputeOrders() {
|
||||||
foreach (Nucleus nucleus in this.clusterNuclei) {
|
foreach (Nucleus input in this._inputs)
|
||||||
// if (nucleus is Cluster cluster) {
|
computeOrders[input] = TopologicalSort2(input);
|
||||||
// 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) {
|
private List<Nucleus> TopologicalSort2(Nucleus startNode) {
|
||||||
Dictionary<Nucleus, int> inDegree = new();
|
Dictionary<Nucleus, int> inDegree = new();
|
||||||
//HashSet<Nucleus> visited = new();
|
HashSet<Nucleus> visited = new();
|
||||||
|
|
||||||
|
// Initialize in-degrees and mark all nodes as unvisited
|
||||||
|
foreach (Nucleus node in this.clusterNuclei)
|
||||||
|
inDegree[node] = 0;
|
||||||
|
|
||||||
// Calculate in-degrees for all nodes reachable from the start node
|
// Calculate in-degrees for all nodes reachable from the start node
|
||||||
Queue<Nucleus> queue = new();
|
Queue<Nucleus> queue = new Queue<Nucleus>();
|
||||||
queue.Enqueue(startNode);
|
queue.Enqueue(startNode);
|
||||||
//visited.Add(startNode);
|
visited.Add(startNode);
|
||||||
inDegree[startNode] = 0;
|
|
||||||
|
|
||||||
while (queue.Count > 0) {
|
while (queue.Count > 0) {
|
||||||
Nucleus current = queue.Dequeue();
|
Nucleus current = queue.Dequeue();
|
||||||
@ -652,24 +575,25 @@ namespace NanoBrain {
|
|||||||
else if (current is Cluster cluster)
|
else if (current is Cluster cluster)
|
||||||
receivers = cluster.CollectReceivers();
|
receivers = cluster.CollectReceivers();
|
||||||
|
|
||||||
|
// if (current is Neuron neuron) {
|
||||||
foreach (Nucleus receiver in receivers) {
|
foreach (Nucleus receiver in receivers) {
|
||||||
if (!inDegree.ContainsKey(receiver)) {
|
if (!visited.Contains(receiver)) {
|
||||||
//visited.Add(receiver);
|
visited.Add(receiver);
|
||||||
inDegree[receiver] = 0;
|
|
||||||
queue.Enqueue(receiver);
|
queue.Enqueue(receiver);
|
||||||
}
|
}
|
||||||
inDegree[receiver]++;
|
inDegree[receiver]++;
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform topological sort on all reachable nodes
|
// Perform topological sort on all reachable nodes
|
||||||
queue.Clear();
|
queue.Clear();
|
||||||
foreach (Nucleus node in inDegree.Keys) {
|
foreach (Nucleus node in visited) {
|
||||||
if (inDegree[node] == 0)
|
if (inDegree[node] == 0)
|
||||||
queue.Enqueue(node);
|
queue.Enqueue(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Nucleus> sortedOrder = new();
|
List<Nucleus> sortedOrder = new List<Nucleus>();
|
||||||
while (queue.Count > 0) {
|
while (queue.Count > 0) {
|
||||||
Nucleus current = queue.Dequeue();
|
Nucleus current = queue.Dequeue();
|
||||||
sortedOrder.Add(current); // Process the node
|
sortedOrder.Add(current); // Process the node
|
||||||
@ -680,18 +604,21 @@ namespace NanoBrain {
|
|||||||
else if (current is Cluster cluster)
|
else if (current is Cluster cluster)
|
||||||
receivers = cluster.CollectReceivers();
|
receivers = cluster.CollectReceivers();
|
||||||
|
|
||||||
|
//if (current is Neuron neuron) {
|
||||||
|
|
||||||
foreach (Nucleus receiver in receivers) {
|
foreach (Nucleus receiver in receivers) {
|
||||||
if (inDegree.ContainsKey(receiver)) {
|
if (visited.Contains(receiver)) {
|
||||||
inDegree[receiver]--;
|
inDegree[receiver]--;
|
||||||
if (inDegree[receiver] == 0) // If all dependencies resolved
|
if (inDegree[receiver] == 0) // If all dependencies resolved
|
||||||
queue.Enqueue(receiver);
|
queue.Enqueue(receiver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for cycles in the graph
|
// Check for cycles in the graph
|
||||||
// if (sortedOrder.Count != visited.Count)
|
if (sortedOrder.Count != visited.Count)
|
||||||
// throw new InvalidOperationException("Graph is not a DAG; a cycle exists.");
|
throw new InvalidOperationException("Graph is not a DAG; a cycle exists.");
|
||||||
|
|
||||||
return sortedOrder;
|
return sortedOrder;
|
||||||
}
|
}
|
||||||
@ -716,9 +643,6 @@ namespace NanoBrain {
|
|||||||
return this._outputs;
|
return this._outputs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void RefreshOutputs() {
|
|
||||||
this._outputs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) {
|
public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) {
|
||||||
foreach (Nucleus receptor in this.clusterNuclei) {
|
foreach (Nucleus receptor in this.clusterNuclei) {
|
||||||
@ -761,41 +685,20 @@ 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
|
#region Receivers
|
||||||
|
|
||||||
public virtual List<Nucleus> CollectReceivers(bool removeDuplicates = false) {
|
public virtual List<Nucleus> CollectReceivers() {
|
||||||
List<Nucleus> receivers = new();
|
List<Nucleus> receivers = new();
|
||||||
foreach (Nucleus outputNucleus in this.clusterNuclei) {
|
foreach (Nucleus outputNucleus in this.clusterNuclei) {
|
||||||
if (outputNucleus is not Neuron output)
|
if (outputNucleus is not Neuron output)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Debug.Log($"output {this.name} {outputNucleus.name}");
|
|
||||||
foreach (Nucleus receiver in output.receivers) {
|
foreach (Nucleus receiver in output.receivers) {
|
||||||
// Debug.Log($"output {receiver.name}");
|
|
||||||
// Only add receivers outside this cluster
|
// Only add receivers outside this cluster
|
||||||
if (receiver.clusterPrefab != this.prefab) {
|
if (receiver.clusterPrefab != this.prefab)
|
||||||
if (removeDuplicates == false || receivers.Contains(receiver) == false)
|
|
||||||
// Debug.Log($" YES");
|
|
||||||
receivers.Add(receiver);
|
receivers.Add(receiver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return receivers;
|
return receivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -814,19 +717,6 @@ namespace NanoBrain {
|
|||||||
}
|
}
|
||||||
return connections;
|
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) {
|
public void MoveReceivers(Cluster newCluster) {
|
||||||
Debug.Log($"Move receivers for {this.name} to {newCluster.name}");
|
Debug.Log($"Move receivers for {this.name} to {newCluster.name}");
|
||||||
@ -862,31 +752,23 @@ namespace NanoBrain {
|
|||||||
// no bias+synapse input state calculation for now...
|
// no bias+synapse input state calculation for now...
|
||||||
|
|
||||||
if (this.computeOrders.ContainsKey(startNucleus) == false) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Nucleus> computeOrder = this.computeOrders[startNucleus];
|
List<Nucleus> computeOrder = this.computeOrders[startNucleus];
|
||||||
//if (startNucleus.trace)
|
if (startNucleus.trace)
|
||||||
Debug.Log($"Update from {startNucleus.name}");
|
Debug.Log($"Update from {startNucleus.name}");
|
||||||
foreach (Nucleus nucleus in computeOrder) {
|
foreach (Nucleus nucleus in computeOrder) {
|
||||||
if (nucleus is not Cluster) {
|
if (nucleus is not Cluster) {
|
||||||
nucleus.UpdateStateIsolated();
|
nucleus.UpdateStateIsolated();
|
||||||
//if (startNucleus.trace && nucleus is Neuron neuron)
|
if (startNucleus.trace && nucleus is Neuron neuron)
|
||||||
Debug.Log($" {nucleus.name}");
|
Debug.Log($" {nucleus.name}[{nucleus.GetHashCode()}]"); // = {neuron.outputValue}");
|
||||||
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
|
// continue in parent
|
||||||
//this.parent?.UpdateFromNucleus(this);
|
this.parent?.UpdateFromNucleus(this);
|
||||||
|
|
||||||
UpdateNuclei();
|
UpdateNuclei();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,10 +33,8 @@ namespace NanoBrain {
|
|||||||
public Neuron(ClusterPrefab prefab, string name) {
|
public Neuron(ClusterPrefab prefab, string name) {
|
||||||
this.clusterPrefab = prefab;
|
this.clusterPrefab = prefab;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
if (this.clusterPrefab != null) {
|
if (this.clusterPrefab != null)
|
||||||
this.clusterPrefab.nuclei.Add(this);
|
this.clusterPrefab.nuclei.Add(this);
|
||||||
this.clusterPrefab.RefreshOutputs();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
Debug.LogError("No prefab when adding neuron to prefab");
|
Debug.LogError("No prefab when adding neuron to prefab");
|
||||||
}
|
}
|
||||||
@ -266,9 +264,6 @@ namespace NanoBrain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void Delete(Nucleus nucleus) {
|
public static void Delete(Nucleus nucleus) {
|
||||||
if (nucleus == null)
|
|
||||||
return;
|
|
||||||
if (nucleus.synapses != null) {
|
|
||||||
foreach (Synapse synapse in nucleus.synapses) {
|
foreach (Synapse synapse in nucleus.synapses) {
|
||||||
if (synapse.neuron is Neuron synapse_nucleus) {
|
if (synapse.neuron is Neuron synapse_nucleus) {
|
||||||
if (synapse_nucleus.receivers.Count > 1) {
|
if (synapse_nucleus.receivers.Count > 1) {
|
||||||
@ -281,7 +276,6 @@ namespace NanoBrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (nucleus is Neuron neuron) {
|
if (nucleus is Neuron neuron) {
|
||||||
foreach (Nucleus receiver in neuron.receivers) {
|
foreach (Nucleus receiver in neuron.receivers) {
|
||||||
if (receiver != null && receiver.synapses != null)
|
if (receiver != null && receiver.synapses != null)
|
||||||
@ -526,8 +520,6 @@ namespace NanoBrain {
|
|||||||
public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) {
|
public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) {
|
||||||
this._receivers.Add(receiverToAdd);
|
this._receivers.Add(receiverToAdd);
|
||||||
receiverToAdd.AddSynapse(this, weight);
|
receiverToAdd.AddSynapse(this, weight);
|
||||||
//Debug.Log($"Add synapse {this.clusterPrefab.name}.{this.name} -> {receiverToAdd.name} --- [{this.receivers.Count}]");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void RemoveReceiver(Nucleus receiverToRemove) {
|
public virtual void RemoveReceiver(Nucleus receiverToRemove) {
|
||||||
|
|||||||
@ -29,7 +29,11 @@ namespace NanoBrain {
|
|||||||
this.weight = weight;
|
this.weight = weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool isSleeping => this.neuron.isSleeping;
|
public bool isSleeping {
|
||||||
|
get {
|
||||||
|
return this.neuron.isSleeping;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user