Cleanup and fix connect neuron

This commit is contained in:
Pascal Serrarens 2026-05-05 10:59:24 +02:00
parent ec2a6b7ae9
commit 79376c30ea
11 changed files with 514 additions and 516 deletions

View File

@ -64,22 +64,23 @@ namespace NanoBrain {
public class GraphEditor : GraphView { public class GraphEditor : GraphView {
protected ClusterPrefab prefab; protected ClusterPrefab prefab;
protected Nucleus currentPrefabNucleus; //protected Nucleus currentPrefabNucleus;
protected override Nucleus currentNucleus { protected override Nucleus currentNucleus {
get => base.currentNucleus; get => base.currentNucleus;
set { set {
base.currentNucleus = value; base.currentNucleus = value;
this.currentPrefabNucleus = value != null ? this.prefab.GetNucleus(value.name) : null; // this.currentPrefabNucleus = value != null ? this.prefab.GetNucleus(value.name) : null;
} }
} }
public GraphEditor(ClusterPrefab prefab) : base(prefab.output.parent) { public GraphEditor(ClusterPrefab prefab) : base(prefab.cluster.defaultOutput.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); //this.currentCluster = new(prefab);
this.currentCluster = prefab.cluster;
} }
public void SetGraph(GameObject gameObject, VisualElement inspectorContainer) { public void SetGraph(GameObject gameObject, VisualElement inspectorContainer) {
@ -120,7 +121,7 @@ namespace NanoBrain {
// create a SerializedObject wrapper so Unity inspector controls work (and Undo) // create a SerializedObject wrapper so Unity inspector controls work (and Undo)
SerializedObject so = new(prefabAsset); SerializedObject so = new(prefabAsset);
foreach (Nucleus nucleus in this.prefab.nuclei) { foreach (Nucleus nucleus in this.prefab.cluster.nuclei) {
nucleus.Initialize(); nucleus.Initialize();
} }
@ -163,7 +164,8 @@ namespace NanoBrain {
GUILayout.Label(nucleusType, headerStyle); GUILayout.Label(nucleusType, headerStyle);
// Nucleus name // Nucleus name
Cluster cluster = this.currentPrefabNucleus as Cluster; //Cluster cluster = this.currentPrefabNucleus as Cluster;
Cluster cluster = this.currentNucleus as Cluster;
if (cluster != null) { if (cluster != null) {
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(this.currentNucleus.parent.name)) if (GUILayout.Button(this.currentNucleus.parent.name))
@ -182,7 +184,7 @@ namespace NanoBrain {
prefabNucleus.name = newName; prefabNucleus.name = newName;
// This changes it in the temporary cluster instance // This changes it in the temporary cluster instance
this.currentNucleus.name = newName; this.currentNucleus.name = newName;
this.prefab.RefreshOutputs(); this.prefab.cluster.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;
} }
@ -281,9 +283,9 @@ namespace NanoBrain {
if (breakOnWake && this.currentNucleus is Neuron currentNeuron) { if (breakOnWake && this.currentNucleus is Neuron currentNeuron) {
if (currentNeuron.isSleeping == false) if (currentNeuron.isSleeping == false)
Debug.Break(); Debug.Break();
trace = EditorGUILayout.Toggle("Trace", trace);
currentNeuron.trace = trace;
} }
trace = EditorGUILayout.Toggle("Trace", trace);
this.currentNucleus.trace = trace;
} }
protected void SynapsesInspector(ref bool anythingChanged) { protected void SynapsesInspector(ref bool anythingChanged) {
@ -293,31 +295,32 @@ namespace NanoBrain {
Neuron.CombinatorType newCombinator = (Neuron.CombinatorType)EditorGUILayout.EnumPopup("Combinator", neuron2.combinator); Neuron.CombinatorType newCombinator = (Neuron.CombinatorType)EditorGUILayout.EnumPopup("Combinator", neuron2.combinator);
anythingChanged |= newCombinator != neuron2.combinator; anythingChanged |= newCombinator != neuron2.combinator;
neuron2.combinator = newCombinator; neuron2.combinator = newCombinator;
}
EditorGUIUtility.wideMode = true; EditorGUIUtility.wideMode = true;
float previousLabelWidth = EditorGUIUtility.labelWidth; float previousLabelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 100; EditorGUIUtility.labelWidth = 100;
Vector3 newBias = EditorGUILayout.Vector3Field("Bias", this.currentNucleus.bias); Vector3 newBias = EditorGUILayout.Vector3Field("Bias", neuron2.bias);
if (newBias != this.currentPrefabNucleus.bias) { if (newBias != neuron2.bias) {
anythingChanged |= newBias != this.currentNucleus.bias; anythingChanged |= newBias != neuron2.bias;
this.currentPrefabNucleus.bias = newBias; neuron2.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.currentPrefabNucleus is Neuron prefabNeuron && prefabNeuron.synapses.Count > 0) {
Synapse[] synapses = this.currentPrefabNucleus.synapses.ToArray(); // Synapse[] synapses = prefabNeuron.synapses.ToArray();
if (this.currentNucleus is Neuron currentNeuron && currentNeuron.synapses.Count > 0) {
Synapse[] synapses = currentNeuron.synapses.ToArray();
foreach (Synapse synapse in synapses) { foreach (Synapse synapse in synapses) {
if (synapse.neuron == null) if (synapse.neuron == null)
continue; continue;
if (array != null) { if (array != null) {
if (synapse.neuron.parent is Cluster iCluster && elementIx > 0) { if (synapse.neuron.parent is Cluster iCluster && elementIx > 0) {
int thisElementIx = Cluster.GetNucleusIndex(iCluster.clusterNuclei, synapse.neuron); int thisElementIx = Cluster.GetNucleusIndex(iCluster.nuclei, synapse.neuron);
if (thisElementIx == elementIx) if (thisElementIx == elementIx)
continue; continue;
else else
@ -332,7 +335,7 @@ namespace NanoBrain {
if (synapse.neuron.parent is Cluster iReceptor) { if (synapse.neuron.parent is Cluster iReceptor) {
array = iReceptor.siblingClusters; array = iReceptor.siblingClusters;
if (iReceptor is Cluster iCluster) if (iReceptor is Cluster iCluster)
elementIx = Cluster.GetNucleusIndex(iCluster.clusterNuclei, synapse.neuron); elementIx = Cluster.GetNucleusIndex(iCluster.nuclei, synapse.neuron);
} }
} }
@ -356,13 +359,13 @@ namespace NanoBrain {
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.clusterPrefab.nuclei.Select(n => n.name).ToArray(); string[] options = synapse.neuron.clusterPrefab.cluster.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) { if (newIndex != selectedIndex) {
// Nucleus selectedNucleus = synapse.neuron.parent.clusterNuclei[newIndex]; // Nucleus selectedNucleus = synapse.neuron.parent.clusterNuclei[newIndex];
// Neuron newNeuron = selectedNucleus as Neuron; // Neuron newNeuron = selectedNucleus as Neuron;
Neuron newNeuron = synapse.neuron.clusterPrefab.nuclei[newIndex] as Neuron; Neuron newNeuron = synapse.neuron.clusterPrefab.cluster.nuclei[newIndex] as Neuron;
ChangeSynapse(synapse, newNeuron); ChangeSynapse(synapse, newNeuron);
} }
} }
@ -370,13 +373,12 @@ namespace NanoBrain {
GUILayout.Label(synapse.neuron.name); GUILayout.Label(synapse.neuron.name);
bool disconnecting = GUILayout.Button("Disconnect", GUILayout.Width(80)); bool disconnecting = GUILayout.Button("Disconnect", GUILayout.Width(80));
if (disconnecting && synapse.neuron is Neuron synapseNeuron) { if (disconnecting) {
synapseNeuron.RemoveReceiver(this.currentNucleus); synapse.neuron.RemoveReceiver(this.currentNucleus);
this.prefab.GarbageCollection(); this.prefab.GarbageCollection();
anythingChanged = true; anythingChanged = true;
} }
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
} }
EditorGUI.indentLevel++; EditorGUI.indentLevel++;
@ -493,11 +495,12 @@ namespace NanoBrain {
if (cluster == null) if (cluster == null)
return false; return false;
IEnumerable<Nucleus> synapseNuclei = this.currentNucleus.synapses Neuron currentNeuron = this.currentNucleus as Neuron;
IEnumerable<Nucleus> synapseNuclei = currentNeuron.synapses
.Where(synapse => synapse.neuron != null) .Where(synapse => synapse.neuron != null)
.Select(synapse => synapse.neuron); .Select(synapse => synapse.neuron);
IEnumerable<Nucleus> nuclei = cluster.nuclei IEnumerable<Nucleus> nuclei = cluster.cluster.nuclei
.Except(synapseNuclei); .Except(synapseNuclei);
IEnumerable<string> nucleiNames = nuclei IEnumerable<string> nucleiNames = nuclei
.Select(n => { .Select(n => {
@ -513,14 +516,36 @@ namespace NanoBrain {
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
if (connecting) { if (connecting) {
Nucleus nucleus = nuclei.ElementAt(selectedConnectNucleus); Nucleus nucleus = nuclei.ElementAt(selectedConnectNucleus);
if (nucleus is Cluster subCluster) // Nucleus prefabNucleus = this.prefab.cluster.nuclei.ElementAt(selectedConnectNucleus);
if (nucleus is Cluster subCluster) {
subCluster.AddArrayReceiver(this.currentNucleus); subCluster.AddArrayReceiver(this.currentNucleus);
else if (nucleus is Neuron neuron) // Cluster prefabSubCluster = prefabNucleus as Cluster;
}
else if (nucleus is Neuron neuron) {
neuron.AddReceiver(this.currentNucleus); neuron.AddReceiver(this.currentNucleus);
// if (prefabNucleus is Neuron prefabNeuron)
// prefabNeuron.AddReceiver(this.currentPrefabNucleus);
}
} }
return connecting; return connecting;
} }
protected virtual void DisconnectNucleus(Neuron nucleus) {
if (this.currentNucleus.clusterPrefab == null)
return;
Neuron currentNeuron = this.currentNucleus as Neuron;
string[] names = currentNeuron.synapses.Select(synapse => synapse.neuron.name).ToArray();
int selectedIndex = -1;
selectedIndex = EditorGUILayout.Popup("Disconnect from", selectedIndex, names);
if (selectedIndex >= 0 && selectedIndex < this.currentNucleus.clusterPrefab.cluster.nuclei.Count) {
Synapse synapse = currentNeuron.synapses[selectedIndex];
synapse.neuron.RemoveReceiver(this.currentNucleus);
// synapse = currentNeuron.synapses[selectedIndex];
// synapse.neuron.RemoveReceiver(this.currentPrefabNucleus);
}
}
protected virtual void DeleteNucleus(Nucleus nucleus) { protected virtual void DeleteNucleus(Nucleus nucleus) {
if (nucleus == null) if (nucleus == null)
return; return;
@ -537,10 +562,10 @@ namespace NanoBrain {
// this.prefab.nuclei.Remove(nucleus); // this.prefab.nuclei.Remove(nucleus);
// Neuron.Delete(nucleus); // Neuron.Delete(nucleus);
this.prefab.RefreshOutputs(); this.prefab.cluster.RefreshOutputs();
this.currentNucleus = this.prefab.output; this.currentNucleus = this.prefab.cluster.defaultOutput;
this.selectedOutput = this.currentNucleus; this.selectedOutput = this.currentNucleus;
} }
@ -596,8 +621,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 {
@ -606,19 +631,6 @@ namespace NanoBrain {
} }
} }
protected virtual void DisconnectNucleus(Neuron nucleus) {
if (this.currentNucleus.clusterPrefab == null)
return;
string[] names = this.currentNucleus.synapses.Select(synapse => synapse.neuron.name).ToArray();
int selectedIndex = -1;
selectedIndex = EditorGUILayout.Popup("Disconnect from", selectedIndex, names);
if (selectedIndex >= 0 && selectedIndex < this.currentNucleus.clusterPrefab.nuclei.Count) {
Synapse synapse = this.currentNucleus.synapses[selectedIndex];
Neuron synapseNeuron = synapse.neuron as Neuron;
synapseNeuron.RemoveReceiver(this.currentNucleus);
}
}
#endregion Synapses #endregion Synapses
#endregion Inspector #endregion Inspector

View File

@ -213,7 +213,8 @@ namespace NanoBrain {
} }
private void DescendGraph(Dag.Node receiver, ref int ix, Dag dag) { private void DescendGraph(Dag.Node receiver, ref int ix, Dag dag) {
foreach (Synapse synapse in receiver.nucleus.synapses) { Neuron receiverNeuron = receiver.nucleus as Neuron;
foreach (Synapse synapse in receiverNeuron.synapses) {
Nucleus nucleus = synapse.neuron; Nucleus nucleus = synapse.neuron;
if (nucleus.parent != null && nucleus.parent != currentNucleus.parent) { if (nucleus.parent != null && nucleus.parent != currentNucleus.parent) {
nucleus = nucleus.parent; nucleus = nucleus.parent;
@ -386,6 +387,9 @@ namespace NanoBrain {
} }
protected void DrawSynapses(Nucleus nucleus, Vector3 parentPos, float size) { protected void DrawSynapses(Nucleus nucleus, Vector3 parentPos, float size) {
if (nucleus is not Neuron neuron)
return;
if (this.selectedSynapseNeuron != null) { if (this.selectedSynapseNeuron != null) {
DrawClusterSynapses(this.selectedSynapseNeuron, parentPos, size); DrawClusterSynapses(this.selectedSynapseNeuron, parentPos, size);
return; return;
@ -398,7 +402,7 @@ namespace NanoBrain {
float maxValue = 0; float maxValue = 0;
int neuronCount = 0; int neuronCount = 0;
List<string> drawnNeuronNames = new(); List<string> drawnNeuronNames = new();
foreach (Synapse synapse in nucleus.synapses) { foreach (Synapse synapse in neuron.synapses) {
if (synapse.neuron == null) if (synapse.neuron == null)
continue; continue;
@ -425,7 +429,7 @@ namespace NanoBrain {
int row = 0; int row = 0;
//List<Neuron> drawnNeurons = new(); //List<Neuron> drawnNeurons = new();
drawnNeuronNames = new(); drawnNeuronNames = new();
foreach (Synapse synapse in nucleus.synapses) { foreach (Synapse synapse in neuron.synapses) {
if (synapse.neuron is null) if (synapse.neuron is null)
continue; continue;

View File

@ -45,7 +45,7 @@ namespace NanoBrain {
/// <param name="name">The name of the Neuron for which the weights are updated</param> /// <param name="name">The name of the Neuron for which the weights are updated</param>
/// <param name="weight">The new Synapse weight</param> /// <param name="weight">The new Synapse weight</param>
public static void UpdateWeight(Cluster brain, string name, float weight) { public static void UpdateWeight(Cluster brain, string name, float weight) {
Nucleus root = brain.defaultOutput; Neuron root = brain.defaultOutput;
foreach (Synapse synapse in root.synapses) { foreach (Synapse synapse in root.synapses) {
if (synapse.neuron.name == name) { if (synapse.neuron.name == name) {
if (synapse.weight != weight) { if (synapse.weight != weight) {

View File

@ -41,7 +41,7 @@ namespace NanoBrain {
public Dictionary<int, Cluster> thingClusters = new(); public Dictionary<int, Cluster> thingClusters = new();
[SerializeReference] [SerializeReference]
public List<Nucleus> clusterNuclei = new(); public List<Nucleus> nuclei = new();
// the nuclei sorted using topological sorting // the nuclei sorted using topological sorting
// to ensure that the cluster is computer in the right order // to ensure that the cluster is computer in the right order
public List<Nucleus> sortedNuclei; public List<Nucleus> sortedNuclei;
@ -58,10 +58,10 @@ namespace NanoBrain {
this.name = prefab.name; this.name = prefab.name;
this.parent = parent; this.parent = parent;
this.parent?.clusterNuclei.Add(this); this.parent?.nuclei.Add(this);
ClonePrefab(); ClonePrefab();
_ = this.inputs; _ = this.inputs;
this.sortedNuclei = TopologicalSort(this.clusterNuclei); this.sortedNuclei = TopologicalSort(this.nuclei);
} }
/// <summary> /// <summary>
@ -75,11 +75,11 @@ namespace NanoBrain {
this.clusterPrefab = parent; this.clusterPrefab = parent;
if (this.clusterPrefab != null) if (this.clusterPrefab != null)
this.clusterPrefab.nuclei.Add(this); this.clusterPrefab.cluster.nuclei.Add(this);
ClonePrefab(); ClonePrefab();
_ = this.inputs; _ = this.inputs;
this.sortedNuclei = TopologicalSort(this.clusterNuclei); this.sortedNuclei = TopologicalSort(this.nuclei);
} }
/// <summary> /// <summary>
@ -88,15 +88,13 @@ namespace NanoBrain {
/// Strange that this does not take any parameters or return values. /// Strange that this does not take any parameters or return values.
/// Where which the clone be found??? /// Where which the clone be found???
private void ClonePrefab() { private void ClonePrefab() {
Nucleus[] prefabNuclei = this.prefab.nuclei.ToArray(); Nucleus[] prefabNuclei = this.prefab.cluster.nuclei.ToArray();
// first clone the nuclei without their connections // first clone the nuclei without their connections
foreach (Nucleus nucleus in prefabNuclei) { foreach (Nucleus nucleus in prefabNuclei) {
nucleus.ShallowCloneTo(this); nucleus.ShallowCloneTo(this);
} }
Nucleus[] clonedNuclei = this.clusterNuclei.ToArray(); Nucleus[] clonedNuclei = this.nuclei.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++) {
@ -113,16 +111,16 @@ namespace NanoBrain {
if (synapseNeuron.clusterPrefab != null && synapseNeuron.clusterPrefab != this.prefab) { if (synapseNeuron.clusterPrefab != null && synapseNeuron.clusterPrefab != this.prefab) {
// Neuron is in another cluster, find the cloned cluster first // Neuron is in another cluster, find the cloned cluster first
ClusterPrefab prefabCluster = synapseNeuron.clusterPrefab; ClusterPrefab prefabCluster = synapseNeuron.clusterPrefab;
Cluster clonedCluster = this.clusterNuclei.Find(n => n.name == prefabCluster.name) as Cluster; Cluster clonedCluster = this.nuclei.Find(n => n.name == prefabCluster.name) as Cluster;
if (clonedCluster == null) if (clonedCluster == null)
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.cluster.nuclei, prefabSynapse.neuron.name);
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;
if (clonedCluster.clusterNuclei[neuronIx] is not Neuron clonedSender) if (clonedCluster.nuclei[neuronIx] is not Neuron clonedSender)
// Could not find the neuron in the cloned cluster // Could not find the neuron in the cloned cluster
continue; continue;
@ -291,16 +289,16 @@ namespace NanoBrain {
if (synapseNeuron.clusterPrefab != null && synapseNeuron.clusterPrefab != this.prefab) { if (synapseNeuron.clusterPrefab != null && synapseNeuron.clusterPrefab != this.prefab) {
// Neuron is in another cluster, find the cloned cluster first // Neuron is in another cluster, find the cloned cluster first
ClusterPrefab prefabCluster = synapseNeuron.clusterPrefab; ClusterPrefab prefabCluster = synapseNeuron.clusterPrefab;
Cluster clonedCluster = this.clusterNuclei.Find(n => n.name == prefabCluster.name) as Cluster; Cluster clonedCluster = this.nuclei.Find(n => n.name == prefabCluster.name) as Cluster;
if (clonedCluster == null) if (clonedCluster == null)
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.cluster.nuclei, prefabSynapse.neuron.name);
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;
if (clonedCluster.clusterNuclei[neuronIx] is not Neuron clonedSender) if (clonedCluster.nuclei[neuronIx] is not Neuron clonedSender)
// Could not find the neuron in the cloned cluster // Could not find the neuron in the cloned cluster
continue; continue;
@ -308,7 +306,7 @@ namespace NanoBrain {
//Debug.Log($"Add synapse {clonedCluster.name}.{clonedSender.name} -> {clonedNeuron.name} [{clonedSender.receivers.Count}]"); //Debug.Log($"Add synapse {clonedCluster.name}.{clonedSender.name} -> {clonedNeuron.name} [{clonedSender.receivers.Count}]");
} }
else { else {
Neuron clonedSender = this.clusterNuclei.Find(n => n.name == prefabSynapse.neuron.name) as Neuron; Neuron clonedSender = this.nuclei.Find(n => n.name == prefabSynapse.neuron.name) as Neuron;
// 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}"); // Debug.Log($"Add synapse {clonedSender.name} -> {clonedNeuron.name}");
@ -378,20 +376,20 @@ namespace NanoBrain {
public override Nucleus Clone(ClusterPrefab parent) { public override Nucleus Clone(ClusterPrefab parent) {
Cluster clone = new(this.prefab, parent); Cluster clone = new(this.prefab, parent);
foreach (Synapse synapse in this.synapses) { // foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.neuron); // Synapse clonedSynapse = clone.AddSynapse(synapse.neuron);
clonedSynapse.weight = synapse.weight; // clonedSynapse.weight = synapse.weight;
} // }
foreach (Nucleus nucleus in this.clusterNuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is Neuron output) { if (nucleus is Neuron output) {
foreach (Nucleus receiver in output.receivers) { foreach (Nucleus receiver in output.receivers) {
int ix = GetNucleusIndex(this.clusterNuclei, output); int ix = GetNucleusIndex(this.nuclei, output);
Debug.Log($"{output.name} -> {receiver.name}: {ix}"); Debug.Log($"{output.name} -> {receiver.name}: {ix}");
if (ix < 0) if (ix < 0)
continue; continue;
if (clone.clusterNuclei[ix] is not Neuron clonedOutput) if (clone.nuclei[ix] is not Neuron clonedOutput)
continue; continue;
clonedOutput.AddReceiver(receiver); clonedOutput.AddReceiver(receiver);
@ -416,23 +414,26 @@ namespace NanoBrain {
private static void CopyAllExternalReceivers(Cluster sourceCluster, Cluster sibling, ClusterPrefab prefabParent, Cluster clonedParent) { private static void CopyAllExternalReceivers(Cluster sourceCluster, Cluster sibling, ClusterPrefab prefabParent, Cluster clonedParent) {
for (int nucleusIx = 0; nucleusIx < sourceCluster.clusterNuclei.Count; nucleusIx++) { for (int nucleusIx = 0; nucleusIx < sourceCluster.nuclei.Count; nucleusIx++) {
Nucleus sourceNucleus = sourceCluster.clusterNuclei[nucleusIx]; Nucleus sourceNucleus = sourceCluster.nuclei[nucleusIx];
if (sourceNucleus is not Neuron sourceNeuron) if (sourceNucleus is not Neuron sourceNeuron)
continue; continue;
if (sibling.clusterNuclei[nucleusIx] is not Neuron clonedNeuron) if (sibling.nuclei[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 sibling
foreach (Nucleus receiver in sourceNeuron.receivers) { foreach (Nucleus receiver in sourceNeuron.receivers) {
int ix = GetNucleusIndex(clonedParent.clusterNuclei, receiver); if (receiver is not Neuron receiverNeuron)
if (ix < 0 || ix >= clonedParent.clusterNuclei.Count) continue;
int ix = GetNucleusIndex(clonedParent.nuclei, receiver);
if (ix < 0 || ix >= clonedParent.nuclei.Count)
continue; continue;
// 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 receiverNeuron.synapses) {
// Find the weight for this synapse // Find the weight for this synapse
if (synapse.neuron == sourceNucleus) { if (synapse.neuron == sourceNucleus) {
weight = synapse.weight; weight = synapse.weight;
@ -603,9 +604,12 @@ namespace NanoBrain {
get { get {
if (this._inputs == null) { if (this._inputs == null) {
this._inputs = new(); this._inputs = new();
foreach (Nucleus nucleus in this.clusterNuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is not Neuron neuron)
continue;
// inputs have no synapses // inputs have no synapses
if (nucleus.synapses.Count == 0) if (neuron.synapses.Count == 0)
this._inputs.Add(nucleus); this._inputs.Add(nucleus);
} }
ComputeOrders(); ComputeOrders();
@ -616,7 +620,7 @@ 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 nucleus in this.nuclei) {
// if (nucleus is Cluster cluster) { // if (nucleus is Cluster cluster) {
// List<Synapse> synapses = this.CollectSynapsesTo(cluster); // List<Synapse> synapses = this.CollectSynapsesTo(cluster);
// foreach (Synapse synapse in synapses) { // foreach (Synapse synapse in synapses) {
@ -628,8 +632,8 @@ namespace NanoBrain {
// // computeOrders[receiver] = TopologicalSort2(receiver); // // computeOrders[receiver] = TopologicalSort2(receiver);
// } // }
// else { // else {
computeOrders[nucleus] = TopologicalSort2(nucleus); computeOrders[nucleus] = TopologicalSort2(nucleus);
Debug.Log($"{this.baseName} Order for {nucleus.name}"); //Debug.Log($"{this.baseName} Order for {nucleus.name}");
// } // }
} }
} }
@ -698,8 +702,8 @@ namespace NanoBrain {
public virtual Neuron defaultOutput {//=> this.nuclei[0] as Nucleus; public virtual Neuron defaultOutput {//=> this.nuclei[0] as Nucleus;
get { get {
if (this.clusterNuclei.Count > 0) if (this.nuclei.Count > 0)
return this.clusterNuclei[0] as Neuron; return this.nuclei[0] as Neuron;
return null; return null;
} }
} }
@ -708,7 +712,7 @@ namespace NanoBrain {
get { get {
if (this._outputs == null) { if (this._outputs == null) {
this._outputs = new(); this._outputs = new();
foreach (Nucleus nucleus in this.clusterNuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is Neuron neuron && neuron.receivers.Count == 0) if (nucleus is Neuron neuron && neuron.receivers.Count == 0)
this._outputs.Add(neuron); this._outputs.Add(neuron);
} }
@ -721,7 +725,7 @@ namespace NanoBrain {
} }
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.nuclei) {
if (receptor is Nucleus nucleus) if (receptor is Nucleus nucleus)
if (nucleus.name == nucleusName) { if (nucleus.name == nucleusName) {
foundNucleus = nucleus; foundNucleus = nucleus;
@ -737,7 +741,7 @@ namespace NanoBrain {
if (dotPosition >= 0) { if (dotPosition >= 0) {
string clusterName = nucleusName[..dotPosition]; string clusterName = nucleusName[..dotPosition];
string clusterName0 = clusterName + ": 0"; string clusterName0 = clusterName + ": 0";
foreach (Nucleus nucleus in this.clusterNuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is Cluster cluster) { if (nucleus is Cluster cluster) {
if (cluster.name == clusterName || cluster.name == clusterName0) { if (cluster.name == clusterName || cluster.name == clusterName0) {
string subNucleusName = nucleusName[(dotPosition + 1)..]; string subNucleusName = nucleusName[(dotPosition + 1)..];
@ -749,7 +753,7 @@ namespace NanoBrain {
} }
else { else {
string nucleusName0 = nucleusName + ": 0"; string nucleusName0 = nucleusName + ": 0";
foreach (Nucleus nucleus in this.clusterNuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is Cluster) { //IReceptor receptor) { if (nucleus is Cluster) { //IReceptor receptor) {
if (nucleus.name == nucleusName | nucleus.name == nucleusName0) if (nucleus.name == nucleusName | nucleus.name == nucleusName0)
return nucleus; return nucleus;
@ -762,16 +766,16 @@ namespace NanoBrain {
} }
public bool DeleteNucleus(Nucleus nucleus) { public bool DeleteNucleus(Nucleus nucleus) {
if (this.clusterNuclei.Contains(nucleus) == false) { if (this.nuclei.Contains(nucleus) == false) {
// Try to find the nucleus by name // Try to find the nucleus by name
if (TryGetNucleus(nucleus.name, out nucleus) == false) if (TryGetNucleus(nucleus.name, out nucleus) == false)
return false; return false;
} }
Neuron.Delete(nucleus); Neuron.Delete(nucleus);
int nucleusIx = this.clusterNuclei.IndexOf(nucleus); int nucleusIx = this.nuclei.IndexOf(nucleus);
this.clusterNuclei.Remove(nucleus); this.nuclei.Remove(nucleus);
this.prefab.nuclei.RemoveAt(nucleusIx); this.prefab.cluster.nuclei.RemoveAt(nucleusIx);
RefreshOutputs(); RefreshOutputs();
return true; return true;
@ -781,7 +785,7 @@ namespace NanoBrain {
public virtual List<Nucleus> CollectReceivers(bool removeDuplicates = false) { public virtual List<Nucleus> CollectReceivers(bool removeDuplicates = false) {
List<Nucleus> receivers = new(); List<Nucleus> receivers = new();
foreach (Nucleus outputNucleus in this.clusterNuclei) { foreach (Nucleus outputNucleus in this.nuclei) {
if (outputNucleus is not Neuron output) if (outputNucleus is not Neuron output)
continue; continue;
@ -802,7 +806,7 @@ namespace NanoBrain {
public List<(Neuron, Nucleus)> CollectConnections() { public List<(Neuron, Nucleus)> CollectConnections() {
List<(Neuron, Nucleus)> connections = new(); List<(Neuron, Nucleus)> connections = new();
foreach (Nucleus outputNucleus in this.clusterNuclei) { foreach (Nucleus outputNucleus in this.nuclei) {
if (outputNucleus is not Neuron output) if (outputNucleus is not Neuron output)
continue; continue;
@ -817,10 +821,10 @@ namespace NanoBrain {
public List<Synapse> CollectSynapsesTo(Cluster otherCluster) { public List<Synapse> CollectSynapsesTo(Cluster otherCluster) {
List<Synapse> collectedSynapses = new(); List<Synapse> collectedSynapses = new();
foreach (Nucleus nucleus in this.clusterNuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is not Neuron neuron) if (nucleus is not Neuron neuron)
continue; continue;
foreach (Synapse synapse in nucleus.synapses) { foreach (Synapse synapse in neuron.synapses) {
if (synapse.neuron.parent == otherCluster) if (synapse.neuron.parent == otherCluster)
collectedSynapses.Add(synapse); collectedSynapses.Add(synapse);
} }
@ -830,7 +834,7 @@ namespace NanoBrain {
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}");
foreach (Nucleus outputNucleus in this.clusterNuclei) { foreach (Nucleus outputNucleus in this.nuclei) {
if (outputNucleus is not Neuron output) if (outputNucleus is not Neuron output)
continue; continue;
@ -846,7 +850,9 @@ namespace NanoBrain {
// Replace synapse with new synapse // Replace synapse with new synapse
// to the new cluster // to the new cluster
Debug.Log($"move {receiver.name} from {this.name}.{output.name} to {newCluster.name}.{newOutput.name}"); Debug.Log($"move {receiver.name} from {this.name}.{output.name} to {newCluster.name}.{newOutput.name}");
Synapse synapse = receiver.GetSynapse(output); if (receiver is not Neuron receiverNeuron)
continue;
Synapse synapse = receiverNeuron.GetSynapse(output);
newOutput.AddReceiver(receiver, synapse.weight); newOutput.AddReceiver(receiver, synapse.weight);
output.RemoveReceiver(receiver); output.RemoveReceiver(receiver);
} }
@ -896,7 +902,7 @@ namespace NanoBrain {
} }
public override void UpdateNuclei() { public override void UpdateNuclei() {
foreach (Nucleus nucleus in this.clusterNuclei) foreach (Nucleus nucleus in this.nuclei)
nucleus.UpdateNuclei(); nucleus.UpdateNuclei();
} }

View File

@ -23,7 +23,7 @@ namespace NanoBrain {
public Neuron(Cluster parent, string name) { public Neuron(Cluster parent, string name) {
this.parent = parent; this.parent = parent;
this.name = name; this.name = name;
this.parent?.clusterNuclei.Add(this); this.parent?.nuclei.Add(this);
} }
/// <summary> /// <summary>
/// Create a new Neuron in a Cluster Prefab /// Create a new Neuron in a Cluster Prefab
@ -34,8 +34,8 @@ namespace NanoBrain {
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.cluster.nuclei.Add(this);
this.clusterPrefab.RefreshOutputs(); this.clusterPrefab.cluster.RefreshOutputs();
} }
else else
Debug.LogError("No prefab when adding neuron to prefab"); Debug.LogError("No prefab when adding neuron to prefab");
@ -43,6 +43,71 @@ namespace NanoBrain {
#region Serialization #region Serialization
/// <summary>
/// The bias
/// </summary>
/// The bias which a value which is always added to the combined value of the neuron
/// It does not have a synapse and therefore no weight of source nucleus
public Vector3 bias = Vector3.zero;
#region Synapses
[SerializeField]
private List<Synapse> _synapses = new();
/// <summary>
/// The synapses of the nucleus
/// </summary>
public List<Synapse> synapses => _synapses;
/// <summary>
/// Add a new synapse to this nuclues
/// </summary>
/// <param name="sendingNucleus">The nucleus from which the signals may originate</param>
/// <param name="weight">The weight applied to the input. Default value = 1</param>
/// <returns>The created Synapse</returns>
/// This will add a new input to this nucleus with the given weight.
public Synapse AddSynapse(Neuron sendingNucleus, float weight = 1) {
Synapse synapse = new(sendingNucleus, weight);
this.synapses.Add(synapse);
return synapse;
}
// public Synapse AddSynapse(ClusterPrefab clusterPrefab, string neuronName, float weight = 1) {
// }
/// <summary>
/// Find a synapse
/// </summary>
/// <param name="sender">The sender of the input to the Synapse</param>
/// <returns>The found Synapse or null when the sender has no synapse to this nucleus.</returns>
public Synapse GetSynapse(Nucleus sender) {
foreach (Synapse synapse in this.synapses)
if (synapse.neuron == sender)
return synapse;
return null;
}
/// <summary>
/// Remove a synapse from a Nucleus
/// </summary>
/// <param name="sendingNucleus">Remote the synapse connecting to this Nucleus</param>
public void RemoveSynapse(Nucleus sendingNucleus) {
this.synapses.RemoveAll(synapse => synapse.neuron == sendingNucleus);
}
#endregion Synapses
/// <summary>
/// Set the bias, recalculate the output and update all Nuclei receiving from this Nucleus
/// </summary>
/// <param name="inputValue"></param>
public virtual void SetBias(Vector3 inputValue) {
this.bias = inputValue;
this.parent.UpdateFromNucleus(this);
}
/// <summary> /// <summary>
/// The type of combinators /// The type of combinators
/// </summary> /// </summary>
@ -231,13 +296,20 @@ namespace NanoBrain {
} }
} }
/// <summary>
/// Toggle for printing debugging trace data
/// </summary>
public bool trace = false;
[NonSerialized] [NonSerialized]
public float lastUpdate = 0; public float lastUpdate = 0;
public readonly float timeToSleep = 1f; public readonly float timeToSleep = 1f;
/// \copydoc NanoBrain::Nucleus::ShallowCloneTo /// \copydoc NanoBrain::Nucleus::ShallowCloneTo
public override Nucleus ShallowCloneTo(Cluster newParent) { public override Nucleus ShallowCloneTo(Cluster newParent) {
Neuron clone = new(newParent, this.name); Neuron clone = new(newParent, this.name) {
// prefabNucleus = this
};
CloneFields(clone); CloneFields(clone);
return clone; return clone;
} }
@ -268,8 +340,8 @@ namespace NanoBrain {
public static void Delete(Nucleus nucleus) { public static void Delete(Nucleus nucleus) {
if (nucleus == null) if (nucleus == null)
return; return;
if (nucleus.synapses != null) { if (nucleus is Neuron neuron) {
foreach (Synapse synapse in nucleus.synapses) { foreach (Synapse synapse in neuron.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) {
// there is another nucleus feeding into this input nucleus // there is another nucleus feeding into this input nucleus
@ -281,19 +353,21 @@ namespace NanoBrain {
} }
} }
} }
}
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 is not Neuron receiverNeuron)
receiver.synapses.RemoveAll(s => s.neuron == nucleus); continue;
if (receiver != null && receiverNeuron.synapses != null)
receiverNeuron.synapses.RemoveAll(s => s.neuron == nucleus);
} }
} }
else if (nucleus is Cluster cluster) { else if (nucleus is Cluster cluster) {
// remove all receivers for this cluster // remove all receivers for this cluster
foreach (Nucleus clusterNucleus in cluster.clusterNuclei) { foreach (Nucleus clusterNucleus in cluster.nuclei) {
if (clusterNucleus is Neuron output) { if (clusterNucleus is Neuron output) {
foreach (Nucleus receiver in output.receivers) { foreach (Nucleus receiver in output.receivers) {
receiver.synapses.RemoveAll(s => s.neuron == output); if (receiver is not Neuron receiverNeuron)
continue;
receiverNeuron.synapses.RemoveAll(s => s.neuron == output);
} }
} }
} }
@ -301,8 +375,8 @@ namespace NanoBrain {
if (nucleus.clusterPrefab != null) { if (nucleus.clusterPrefab != null) {
nucleus.clusterPrefab.nuclei.RemoveAll(n => n == nucleus); nucleus.clusterPrefab.cluster.nuclei.RemoveAll(n => n == nucleus);
nucleus.clusterPrefab.RefreshOutputs(); nucleus.clusterPrefab.cluster.RefreshOutputs();
nucleus.clusterPrefab.GarbageCollection(); nucleus.clusterPrefab.GarbageCollection();
} }
} }
@ -524,15 +598,25 @@ namespace NanoBrain {
} }
public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) { public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) {
this._receivers.Add(receiverToAdd); if (receiverToAdd is not Neuron receiverNeuron)
receiverToAdd.AddSynapse(this, weight); return;
this._receivers.Add(receiverNeuron);
receiverNeuron.AddSynapse(this, weight);
//Debug.Log($"Add synapse {this.clusterPrefab.name}.{this.name} -> {receiverToAdd.name} --- [{this.receivers.Count}]"); //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) {
this._receivers.RemoveAll(receiver => receiver == receiverToRemove); if (receiverToRemove is not Neuron receiverNeuron)
receiverToRemove.synapses.RemoveAll(synapse => synapse.neuron == this); return;
this._receivers.RemoveAll(receiver => receiver == receiverNeuron);
receiverNeuron.synapses.RemoveAll(synapse => synapse.neuron == this);
// Nucleus prefabReceiver = receiverToRemove.prefabNucleus;
// if (this.prefabNucleus is Neuron prefabNeuron && prefabReceiver != null) {
// prefabNeuron.receivers.RemoveAll(receiver => receiver == prefabReceiver);
// prefabReceiver.synapses.RemoveAll(synapse => synapse.neuron == prefabNeuron);
// }
} }

View File

@ -17,6 +17,9 @@ public abstract class Nucleus {
/// </summary> /// </summary>
public string name; public string name;
// [NonSerialized]
// public Nucleus prefabNucleus;
/// <summary> /// <summary>
/// The cluster prefab in which the nucleus is located /// The cluster prefab in which the nucleus is located
/// </summary> /// </summary>
@ -31,7 +34,7 @@ public abstract class Nucleus {
/// <summary> /// <summary>
/// Toggle for printing debugging trace data /// Toggle for printing debugging trace data
/// </summary> /// </summary>
public bool trace = false; //public bool trace = false;
/// <summary> /// <summary>
/// Function to make a partial clone of this nucleus /// Function to make a partial clone of this nucleus
@ -61,60 +64,60 @@ public abstract class Nucleus {
public virtual void Initialize() {} public virtual void Initialize() {}
#region Synapses // #region Synapses
/// <summary> // /// <summary>
/// The bias of the nucleus // /// The bias of the nucleus
/// </summary> // /// </summary>
/// The bias which a value which is always added to the combined value of the nucleus // /// The bias which a value which is always added to the combined value of the nucleus
/// It does not have a synapse and therefore no weight of source nucleus // /// It does not have a synapse and therefore no weight of source nucleus
public Vector3 bias = Vector3.zero; // //public Vector3 bias = Vector3.zero;
[SerializeField] // [SerializeField]
private List<Synapse> _synapses = new(); // private List<Synapse> _synapses = new();
/// <summary> // /// <summary>
/// The synapses of the nucleus // /// The synapses of the nucleus
/// </summary> // /// </summary>
public List<Synapse> synapses => _synapses; // public List<Synapse> synapses => _synapses;
/// <summary> // /// <summary>
/// Add a new synapse to this nuclues // /// Add a new synapse to this nuclues
/// </summary> // /// </summary>
/// <param name="sendingNucleus">The nucleus from which the signals may originate</param> // /// <param name="sendingNucleus">The nucleus from which the signals may originate</param>
/// <param name="weight">The weight applied to the input. Default value = 1</param> // /// <param name="weight">The weight applied to the input. Default value = 1</param>
/// <returns>The created Synapse</returns> // /// <returns>The created Synapse</returns>
/// This will add a new input to this nucleus with the given weight. // /// This will add a new input to this nucleus with the given weight.
public Synapse AddSynapse(Neuron sendingNucleus, float weight = 1) { // public Synapse AddSynapse(Neuron sendingNucleus, float weight = 1) {
Synapse synapse = new(sendingNucleus, weight); // Synapse synapse = new(sendingNucleus, weight);
this.synapses.Add(synapse); // this.synapses.Add(synapse);
return synapse; // return synapse;
} // }
// public Synapse AddSynapse(ClusterPrefab clusterPrefab, string neuronName, float weight = 1) { // // public Synapse AddSynapse(ClusterPrefab clusterPrefab, string neuronName, float weight = 1) {
// } // // }
/// <summary> // /// <summary>
/// Find a synapse // /// Find a synapse
/// </summary> // /// </summary>
/// <param name="sender">The sender of the input to the Synapse</param> // /// <param name="sender">The sender of the input to the Synapse</param>
/// <returns>The found Synapse or null when the sender has no synapse to this nucleus.</returns> // /// <returns>The found Synapse or null when the sender has no synapse to this nucleus.</returns>
public Synapse GetSynapse(Nucleus sender) { // public Synapse GetSynapse(Nucleus sender) {
foreach (Synapse synapse in this.synapses) // foreach (Synapse synapse in this.synapses)
if (synapse.neuron == sender) // if (synapse.neuron == sender)
return synapse; // return synapse;
return null; // return null;
} // }
/// <summary> // /// <summary>
/// Remove a synapse from a Nucleus // /// Remove a synapse from a Nucleus
/// </summary> // /// </summary>
/// <param name="sendingNucleus">Remote the synapse connecting to this Nucleus</param> // /// <param name="sendingNucleus">Remote the synapse connecting to this Nucleus</param>
public void RemoveSynapse(Nucleus sendingNucleus) { // public void RemoveSynapse(Nucleus sendingNucleus) {
this.synapses.RemoveAll(synapse => synapse.neuron == sendingNucleus); // this.synapses.RemoveAll(synapse => synapse.neuron == sendingNucleus);
} // }
#endregion Synapses // #endregion Synapses
#region Update #region Update
@ -129,14 +132,14 @@ public abstract class Nucleus {
public virtual void UpdateNuclei() { public virtual void UpdateNuclei() {
} }
/// <summary> // /// <summary>
/// Set the bias, recalculate the output and update all Nuclei receiving from this Nucleus // /// Set the bias, recalculate the output and update all Nuclei receiving from this Nucleus
/// </summary> // /// </summary>
/// <param name="inputValue"></param> // /// <param name="inputValue"></param>
public virtual void SetBias(Vector3 inputValue) { // public virtual void SetBias(Vector3 inputValue) {
this.bias = inputValue; // this.bias = inputValue;
this.parent.UpdateFromNucleus(this); // this.parent.UpdateFromNucleus(this);
} // }
/// <summary> /// <summary>
/// Process an external stimulus /// Process an external stimulus

View File

@ -8,87 +8,94 @@ namespace NanoBrain {
/// </summary> /// </summary>
[CreateAssetMenu(menuName = "Passer/Cluster")] [CreateAssetMenu(menuName = "Passer/Cluster")]
public class ClusterPrefab : ScriptableObject { public class ClusterPrefab : ScriptableObject {
/// The nuclei in this cluster
[SerializeReference]
// This list should not include any clusters...
public List<Nucleus> nuclei = new();
/// <summary> public Cluster cluster;
/// The output of this cluster /*
/// </summary> /// The nuclei in this cluster
/// <deprecated>This only returens the first(default) nucleus. Use outputs[0] instead</deprecated> [SerializeReference]
public virtual Nucleus output => this.nuclei[0] as Nucleus; // This list should not include any clusters...
public List<Nucleus> nuclei = new();
/// <summary> /// <summary>
/// The nuclei in this cluster which are meant for receiving signals from outside the cluster /// The output of this cluster
/// </summary> /// </summary>
/// <remark>This is currently the nuclei which do not have any incoming synapse</remark> /// <deprecated>This only returens the first(default) nucleus. Use outputs[0] instead</deprecated>
public List<Nucleus> _inputs = null; public virtual Nucleus output => this.nuclei[0] as Nucleus;
public virtual List<Nucleus> inputs {
get { /// <summary>
if (this._inputs == null) { /// The nuclei in this cluster which are meant for receiving signals from outside the cluster
this._inputs = new(); /// </summary>
foreach (Nucleus receptor in this.nuclei) { /// <remark>This is currently the nuclei which do not have any incoming synapse</remark>
if (receptor is Nucleus nucleus) { public List<Nucleus> _inputs = null;
// inputs have no incoming synapses yet. public virtual List<Nucleus> inputs {
if (nucleus.synapses.Count == 0) get {
this._inputs.Add(nucleus); if (this._inputs == null) {
this._inputs = new();
foreach (Nucleus receptor in this.nuclei) {
if (receptor is Nucleus nucleus) {
// inputs have no incoming synapses yet.
if (nucleus.synapses.Count == 0)
this._inputs.Add(nucleus);
}
}
} }
return this._inputs;
} }
} }
return this._inputs; /// <summary>
} /// The nuclei in this cluster which are meant for sending signals onward
} /// </summary>
/// <summary> private List<Nucleus> _outputs = null;
/// The nuclei in this cluster which are meant for sending signals onward public List<Nucleus> outputs {
/// </summary> get {
private List<Nucleus> _outputs = null; if (this._outputs == null)
public List<Nucleus> outputs { RefreshOutputs();
get { return this._outputs;
if (this._outputs == null) }
RefreshOutputs(); }
return this._outputs; /// <summary>
} /// Redetermine the outpus in the cluster
} /// </summary>
/// <summary> public void RefreshOutputs() {
/// Redetermine the outpus in the cluster this._outputs = new();
/// </summary> foreach (Nucleus nucleus in this.nuclei) {
public void RefreshOutputs() { if (nucleus is Neuron neuron && neuron.receivers.Count == 0)
this._outputs = new(); this._outputs.Add(nucleus);
foreach (Nucleus nucleus in this.nuclei) { }
if (nucleus is Neuron neuron && neuron.receivers.Count == 0) }
this._outputs.Add(nucleus); */
}
}
/// <summary> /// <summary>
/// Retrieve a nucleus in this cluster /// Retrieve a nucleus in this cluster
/// </summary> /// </summary>
/// <param name="nucleusName">The name of the nucleus</param> /// <param name="nucleusName">The name of the nucleus</param>
/// <returns>The Nucleus with the given name or null if no such Nucleus could be found</returns> /// <returns>The Nucleus with the given name or null if no such Nucleus could be found</returns>
public Nucleus GetNucleus(string nucleusName) { public Nucleus GetNucleus(string nucleusName) {
foreach (Nucleus nucleus in this.nuclei) { return cluster.GetNucleus(nucleusName);
if (nucleus.name == nucleusName) // foreach (Nucleus nucleus in this.nuclei) {
return nucleus; // if (nucleus.name == nucleusName)
} // return nucleus;
return null; // }
// return null;
} }
// Call this function to ensure that there is at least one nucleus // Call this function to ensure that there is at least one nucleus
// This is an invariant and should be ensured before the nucleus is used // This is an invariant and should be ensured before the nucleus is used
// because output requires it. // because output requires it.
public void EnsureInitialization() { public void EnsureInitialization() {
nuclei ??= new List<Nucleus>(); this.cluster.nuclei ??= new List<Nucleus>();
if (nuclei.Count == 0) if (this.cluster.nuclei.Count <= 0)
new Neuron(this, "Output"); // Every cluster should have at least 1 neuron new Neuron(this, "Output"); // Every cluster should have at least 1 neuron
// nuclei ??= new List<Nucleus>();
// if (nuclei.Count == 0)
// new Neuron(this, "Output"); // Every cluster should have at least 1 neuron
} }
public void GarbageCollection() { public void GarbageCollection() {
HashSet<Nucleus> visitedNuclei = new(); // HashSet<Nucleus> visitedNuclei = new();
foreach (Nucleus output in this.outputs) // foreach (Nucleus output in this.outputs)
MarkNuclei(visitedNuclei, output); // MarkNuclei(visitedNuclei, output);
//Debug.Log($"Garbage collection found {visitedNuclei.Count} Nuclei"); // //Debug.Log($"Garbage collection found {visitedNuclei.Count} Nuclei");
this.nuclei.RemoveAll(nucleus => visitedNuclei.Contains(nucleus) == false); // this.nuclei.RemoveAll(nucleus => visitedNuclei.Contains(nucleus) == false);
} }
public void MarkNuclei(HashSet<Nucleus> visitedNuclei, Nucleus nucleus) { public void MarkNuclei(HashSet<Nucleus> visitedNuclei, Nucleus nucleus) {
@ -99,43 +106,45 @@ namespace NanoBrain {
visitedNuclei.Add(nucleus.parent); visitedNuclei.Add(nucleus.parent);
else else
visitedNuclei.Add(nucleus); visitedNuclei.Add(nucleus);
if (nucleus.synapses != null) { if (nucleus is Neuron neuron) {
HashSet<Synapse> visitedSynapses = new(); if (neuron.synapses != null) {
foreach (Synapse synapse in nucleus.synapses) { HashSet<Synapse> visitedSynapses = new();
if (synapse != null && synapse.neuron != null) { foreach (Synapse synapse in neuron.synapses) {
visitedSynapses.Add(synapse); if (synapse != null && synapse.neuron != null) {
if (synapse.neuron is Nucleus synapse_nucleus) visitedSynapses.Add(synapse);
MarkNuclei(visitedNuclei, synapse_nucleus); if (synapse.neuron is Nucleus synapse_nucleus)
MarkNuclei(visitedNuclei, synapse_nucleus);
}
} }
neuron.synapses.RemoveAll(synapse => visitedSynapses.Contains(synapse) == false);
} }
nucleus.synapses.RemoveAll(synapse => visitedSynapses.Contains(synapse) == false); if (neuron.receivers != null) {
} HashSet<Nucleus> visitedReceivers = new();
if (nucleus is Neuron neuron && neuron.receivers != null) { foreach (Nucleus receiver in neuron.receivers) {
HashSet<Nucleus> visitedReceivers = new(); if (receiver != null && receiver != null) {
foreach (Nucleus receiver in neuron.receivers) { visitedReceivers.Add(receiver);
if (receiver != null && receiver != null) { visitedNuclei.Add(receiver);
visitedReceivers.Add(receiver); }
visitedNuclei.Add(receiver);
} }
neuron.receivers.RemoveAll(receiver => visitedReceivers.Contains(receiver) == false);
} }
neuron.receivers.RemoveAll(receiver => visitedReceivers.Contains(receiver) == false);
} }
} }
public virtual void UpdateNuclei() { // public virtual void UpdateNuclei() {
foreach (Nucleus nucleus in this.nuclei) // foreach (Nucleus nucleus in this.nuclei)
nucleus.UpdateNuclei(); // nucleus.UpdateNuclei();
} // }
public int GetNucleusIndex(Nucleus receiver) { // public int GetNucleusIndex(Nucleus receiver) {
int ix = 0; // int ix = 0;
foreach (Nucleus nucleus in this.nuclei) { // foreach (Nucleus nucleus in this.nuclei) {
if (receiver == nucleus) // if (receiver == nucleus)
return ix; // return ix;
ix++; // ix++;
} // }
return -1; // return -1;
} // }
} }
} }

View File

@ -43,11 +43,11 @@ namespace CreatureControl {
/// </summary> /// </summary>
/// It is used by the brain to do things periodically /// It is used by the brain to do things periodically
/// like placing pheromones /// like placing pheromones
public Nucleus beat; public Neuron beat;
public Nucleus pheromoneSteering; public Neuron pheromoneSteering;
public Nucleus hitLeft; public Neuron hitLeft;
public Nucleus hitRight; public Neuron hitRight;
public Neuron foodReceptor; public Neuron foodReceptor;
public Neuron homeReceptor; public Neuron homeReceptor;
@ -60,6 +60,22 @@ namespace CreatureControl {
base.Awake(); base.Awake();
this.nanoBrain = GetComponentInChildren<Brain>(); this.nanoBrain = GetComponentInChildren<Brain>();
Cluster brain = this.nanoBrain.brain;
if (brain != null) {
//--- brain inputs
this.beat = brain.GetNucleus("Beat") as Neuron;
this.hitLeft = brain.GetNucleus("Hit Left") as Neuron;
this.hitRight = brain.GetNucleus("Hit Right") as Neuron;
this.foodReceptor = brain.GetNucleus("Food Receptor") as Neuron;
this.homeReceptor = brain.GetNucleus("Home Receptor") as Neuron;
this.pheromoneSteering = brain.GetNucleus("Pheromone Steering") as Neuron;
//--- brain outputs
this.targetDirection = brain.GetNucleus("Output") as Neuron;
this.hasFood = brain.GetNucleus("Having Food") as Neuron;
}
} }
#endregion Init #endregion Init
@ -71,17 +87,6 @@ namespace CreatureControl {
Cluster brain = this.nanoBrain.brain; Cluster brain = this.nanoBrain.brain;
if (brain != null) { if (brain != null) {
//--- brain inputs
this.beat = brain.GetNucleus("Beat");
this.hitLeft = brain.GetNucleus("Hit Left");
this.hitRight = brain.GetNucleus("Hit Right");
this.foodReceptor = brain.GetNucleus("Food Receptor") as Neuron;
this.homeReceptor = brain.GetNucleus("Home Receptor") as Neuron;
this.pheromoneSteering = brain.GetNucleus("Pheromone Steering");
//--- brain outputs
this.targetDirection = brain.defaultOutput;
// Try to find the Home Pheromones Neuron // Try to find the Home Pheromones Neuron
if (brain.GetNucleus("Home Pheromones") is Neuron homePheromones) if (brain.GetNucleus("Home Pheromones") is Neuron homePheromones)
@ -92,7 +97,6 @@ namespace CreatureControl {
// and call PlaceFoodPheromone when it is firing // and call PlaceFoodPheromone when it is firing
foodPheromones.WhenFiring += PlaceFoodPheromone; foodPheromones.WhenFiring += PlaceFoodPheromone;
this.hasFood = brain.GetNucleus("Having Food") as Neuron;
} }
// Initialize the callbacks for the antenna colliders // Initialize the callbacks for the antenna colliders

View File

@ -1,233 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 60a957541c24c57e78018c202ebb1d9b, type: 3}
m_Name: Foraging
m_EditorClassIdentifier:
nuclei:
- rid: 2642584373999960064
- rid: 2642584373999960065
references:
version: 2
RefIds:
- rid: -2
type: {class: , ns: , asm: }
- rid: 2642584373999960064
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: Output
clusterPrefab: {fileID: 11400000}
parent:
rid: -2
trace: 0
bias: {x: 0, y: 0, z: 0}
_synapses: []
combinator: 0
_curvePreset: 0
curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
curveMax: 1
_receivers: []
- rid: 2642584373999960065
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: Collision Steering
clusterPrefab: {fileID: 11400000}
parent:
rid: -2
trace: 0
bias: {x: 0, y: 0, z: 0}
_synapses: []
combinator: 0
_curvePreset: 0
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1000
value: 1000
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
curveMax: 1
_receivers:
- rid: 2642584373999960066
- rid: 2642584373999960068
- rid: 2642584373999960066
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: Output
clusterPrefab: {fileID: 11400000}
parent:
rid: 2642584373999960067
trace: 0
bias: {x: 0, y: 0, z: 0}
_synapses:
- neuron:
rid: 2642584373999960065
weight: 1
combinator: 0
_curvePreset: 0
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1000
value: 1000
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
curveMax: 1
_receivers: []
- rid: 2642584373999960067
type: {class: Cluster, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: Foraging
clusterPrefab: {fileID: 0}
parent:
rid: -2
trace: 0
bias: {x: 0, y: 0, z: 0}
_synapses: []
prefab: {fileID: 11400000}
siblingClusters: []
instanceCount: 1
clusterNuclei:
- rid: 2642584373999960066
- rid: 2642584373999960068
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: Output
clusterPrefab: {fileID: 11400000}
parent:
rid: 2642584373999960069
trace: 0
bias: {x: 0, y: 0, z: 0}
_synapses:
- neuron:
rid: 2642584373999960065
weight: 1
combinator: 0
_curvePreset: 0
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1000
value: 1000
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
curveMax: 1
_receivers: []
- rid: 2642584373999960069
type: {class: Cluster, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: Foraging
clusterPrefab: {fileID: 0}
parent:
rid: -2
trace: 0
bias: {x: 0, y: 0, z: 0}
_synapses: []
prefab: {fileID: 11400000}
siblingClusters: []
instanceCount: 1
clusterNuclei:
- rid: 2642584373999960068
- rid: 2642584373999960070
- rid: 2642584373999960070
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: Collision Steering
clusterPrefab: {fileID: 11400000}
parent:
rid: 2642584373999960069
trace: 0
bias: {x: 0, y: 0, z: 0}
_synapses: []
combinator: 0
_curvePreset: 0
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1000
value: 1000
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
curveMax: 1
_receivers: []

View File

@ -0,0 +1,109 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 60a957541c24c57e78018c202ebb1d9b, type: 3}
m_Name: New Cluster Prefab
m_EditorClassIdentifier:
cluster:
name:
clusterPrefab: {fileID: 0}
parent:
rid: -2
prefab: {fileID: 0}
instanceCount: 0
nuclei:
- rid: 4201949831649034293
- rid: 4201949831649034294
references:
version: 2
RefIds:
- rid: -2
type: {class: , ns: , asm: }
- rid: 4201949831649034293
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: Output
clusterPrefab: {fileID: 11400000}
parent:
rid: -2
bias: {x: 0, y: 0, z: 0}
_synapses:
- neuron:
rid: 4201949831649034294
weight: 1
combinator: 0
_curvePreset: 0
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1000
value: 1000
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
curveMax: 1
trace: 0
_receivers: []
- rid: 4201949831649034294
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
data:
name: New neuron
clusterPrefab: {fileID: 11400000}
parent:
rid: -2
bias: {x: 0, y: 0, z: 0}
_synapses: []
combinator: 0
_curvePreset: 0
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1000
value: 1000
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
curveMax: 1
trace: 0
_receivers:
- rid: 4201949831649034293

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 81292defec7ff5278a48a5d154659d00 guid: c8e4b0990eb7dbbc4bee34addd9cd2b8
NativeFormatImporter: NativeFormatImporter:
externalObjects: {} externalObjects: {}
mainObjectFileID: 11400000 mainObjectFileID: 11400000