Multi smell works

This commit is contained in:
Pascal Serrarens 2026-05-06 17:39:56 +02:00
parent 33ea14bb72
commit 32b58852d4
5 changed files with 177 additions and 276 deletions

View File

@ -165,25 +165,10 @@ namespace NanoBrain {
GUILayout.Label(nucleusType, headerStyle); GUILayout.Label(nucleusType, headerStyle);
// Nucleus name // Nucleus name
//Cluster cluster = this.currentPrefabNucleus as Cluster; string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
Cluster cluster = this.currentNucleus as Cluster; if (newName != this.currentNucleus.name) {
if (cluster != null) { this.currentNucleus.name = newName;
EditorGUILayout.BeginHorizontal(); anythingChanged = true;
if (GUILayout.Button(this.currentNucleus.parent.name))
OnClusterClick(cluster);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
EditorGUI.EndDisabledGroup();
if (GUILayout.Button("Reimport"))
ReimportCluster(cluster);
EditorGUILayout.EndHorizontal();
}
else {
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
if (newName != this.currentNucleus.name) {
this.currentNucleus.name = newName;
anythingChanged = true;
}
} }
// Current output value // Current output value
@ -202,7 +187,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
@ -277,8 +262,8 @@ 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); // trace = EditorGUILayout.Toggle("Trace", trace);
currentNeuron.trace = trace; // currentNeuron.trace = trace;
} }
} }
@ -304,8 +289,6 @@ namespace NanoBrain {
Nucleus[] array = null; Nucleus[] array = null;
int elementIx = -1; int elementIx = -1;
//if (this.currentPrefabNucleus is Neuron prefabNeuron && prefabNeuron.synapses.Count > 0) {
// Synapse[] synapses = prefabNeuron.synapses.ToArray();
if (this.currentNucleus is Neuron currentNeuron && currentNeuron.synapses.Count > 0) { if (this.currentNucleus is Neuron currentNeuron && currentNeuron.synapses.Count > 0) {
Synapse[] synapses = currentNeuron.synapses.ToArray(); Synapse[] synapses = currentNeuron.synapses.ToArray();
foreach (Synapse synapse in synapses) { foreach (Synapse synapse in synapses) {
@ -345,19 +328,19 @@ namespace NanoBrain {
else { else {
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
if (synapse.neuron.parent.prefab != this.currentNucleus.parent.prefab) { if (synapse.neuron.parent != this.currentNucleus.parent) {
// If it is a different cluster // If it is a different cluster
GUIStyle labelStyle = new(GUI.skin.label); GUIStyle labelStyle = new(GUI.skin.label);
float labelWidth = 200; float labelWidth = 200;
if (synapse.neuron.parent.prefab != null) { if (synapse.neuron.parent != null) {
labelWidth = labelStyle.CalcSize(new GUIContent($"{synapse.neuron.parent.prefab.name}.")).x; labelWidth = labelStyle.CalcSize(new GUIContent($"{synapse.neuron.parent.name}.")).x;
GUILayout.Label($"{synapse.neuron.parent.prefab.name}", GUILayout.Width(labelWidth)); GUILayout.Label($"{synapse.neuron.parent.name}", GUILayout.Width(labelWidth));
} }
string[] options = synapse.neuron.parent.prefab.cluster.nuclei.Select(n => n.name).ToArray(); string[] options = synapse.neuron.parent.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) {
Neuron newNeuron = synapse.neuron.parent.prefab.cluster.nuclei[newIndex] as Neuron; Neuron newNeuron = synapse.neuron.parent.nuclei[newIndex] as Neuron;
ChangeSynapse(synapse, newNeuron); ChangeSynapse(synapse, newNeuron);
} }
} }
@ -437,7 +420,7 @@ namespace NanoBrain {
} }
protected virtual void AddNeuronInput(Nucleus nucleus) { protected virtual void AddNeuronInput(Nucleus nucleus) {
Neuron newNeuron = new (this.currentCluster, "New Neuron"); Neuron newNeuron = new(this.currentCluster, "New Neuron");
//Neuron newNeuroid = new(this.prefab.cluster, "New neuron"); //Neuron newNeuroid = new(this.prefab.cluster, "New neuron");
newNeuron.AddReceiver(nucleus); newNeuron.AddReceiver(nucleus);
this.currentNucleus = newNeuron; this.currentNucleus = newNeuron;
@ -453,7 +436,7 @@ namespace NanoBrain {
ClusterPickerWindow.ShowPicker(brain => OnClusterPicked(nucleus, brain), "Select Cluster"); ClusterPickerWindow.ShowPicker(brain => OnClusterPicked(nucleus, brain), "Select Cluster");
} }
private void OnClusterPicked(Nucleus nucleus, ClusterPrefab selectedPrefab) { private void OnClusterPicked(Nucleus nucleus, ClusterPrefab selectedPrefab) {
Cluster subclusterInstance = new(selectedPrefab, this.prefab); Cluster subclusterInstance = new(selectedPrefab, this.currentCluster);
subclusterInstance.defaultOutput.AddReceiver(nucleus); subclusterInstance.defaultOutput.AddReceiver(nucleus);
} }
@ -557,11 +540,6 @@ namespace NanoBrain {
AddInput(selectedType, this.currentNucleus); AddInput(selectedType, this.currentNucleus);
} }
return connecting; return connecting;
// if (selectedType == Nucleus.Type.None)
// return false;
// AddInput(selectedType, this.currentNucleus);
// return true;
} }
protected virtual void ChangeSynapse(Synapse synapse, Neuron newNucleus) { protected virtual void ChangeSynapse(Synapse synapse, Neuron newNucleus) {

View File

@ -108,7 +108,7 @@ namespace NanoBrain {
this.gameObject = gameObject; this.gameObject = gameObject;
if (this.currentCluster == null) if (this.currentCluster == null)
return; return;
if (Application.isPlaying == false) if (Application.isPlaying == false)
this.serializedBrain = new SerializedObject(this.currentCluster.prefab); this.serializedBrain = new SerializedObject(this.currentCluster.prefab);
this.selectedOutput = this.currentCluster.outputs[0]; this.selectedOutput = this.currentCluster.outputs[0];
@ -488,14 +488,15 @@ namespace NanoBrain {
maxValue = 1; maxValue = 1;
float brightness = siblingNeuron.outputMagnitude / maxValue; float brightness = siblingNeuron.outputMagnitude / maxValue;
color = new Color(brightness, brightness, brightness, 1f); color = new Color(brightness, brightness, brightness, 1f);
} DrawNucleus(siblingNeuron, position, size, color); }
DrawNucleus(siblingNeuron, position, size, color);
GUIStyle style = new(EditorStyles.label) { GUIStyle style = new(EditorStyles.label) {
alignment = TextAnchor.UpperCenter, alignment = TextAnchor.UpperCenter,
normal = { textColor = Color.white }, normal = { textColor = Color.white },
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
}; };
Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron
string name = $"{sibling.baseName}.{nucleus.name}"; string name = $"{sibling.baseName}\n{nucleus.name}";
Handles.Label(labelPos, name, style); Handles.Label(labelPos, name, style);
row++; row++;
} }
@ -505,7 +506,7 @@ namespace NanoBrain {
protected void DrawOutputs(Vector2 parentPos, float size) { protected void DrawOutputs(Vector2 parentPos, float size) {
if (this.currentCluster == null) if (this.currentCluster == null)
return; return;
// 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
float maxValue = 0; float maxValue = 0;
@ -612,18 +613,12 @@ namespace NanoBrain {
if (nucleus.parent != null && currentNucleus != null && nucleus.parent != currentNucleus.parent && nucleus.parent is Cluster parentCluster1) { if (nucleus.parent != null && currentNucleus != null && nucleus.parent != currentNucleus.parent && nucleus.parent is Cluster parentCluster1) {
// This neuron is part of another cluster // This neuron is part of another cluster
parentCluster1.name ??= ""; parentCluster1.name ??= "";
string baseName = "";
int colonPos = parentCluster1.name.IndexOf(":"); int colonPos = parentCluster1.name.IndexOf(":");
string baseName;
if (colonPos > 0 && colonPos < parentCluster1.name.Length - 2) if (colonPos > 0 && colonPos < parentCluster1.name.Length - 2)
baseName = parentCluster1.name[..colonPos] + "."; baseName = parentCluster1.name[..colonPos] + "\n";
else else
baseName = parentCluster1.name + "."; baseName = parentCluster1.name + "\n";
// if (colonPos > 0 && colonPos < parentCluster1.name.Length - 2) {
// // if it is an array, we should not show the :0 of the first element
// //baseName = baseName[..colonPos];
// Handles.Label(labelPos, baseName + nucleus.name, style);
// }
// else
Handles.Label(labelPos, baseName + nucleus.name, style); Handles.Label(labelPos, baseName + nucleus.name, style);
} }
else { else {
@ -858,22 +853,34 @@ namespace NanoBrain {
void OnSceneGUI(SceneView sceneView) { void OnSceneGUI(SceneView sceneView) {
if (this.gameObject != null) { if (this.gameObject != null) {
// if (this.currentNucleus is IReceptor receptor) {
// foreach (Nucleus nucleus in receptor.nucleiArray) { Handles.color = Color.yellow;
// if (nucleus is Neuron neuron) { if (this.selectedSynapseNeuron != null) {
// Vector3 worldVector = this.gameObject.transform.TransformVector(neuron.outputValue); foreach (Cluster sibling in this.selectedSynapseNeuron.parent.siblingClusters) {
// Handles.color = Color.yellow; Neuron siblingNeuron = sibling.GetNucleus(this.selectedSynapseNeuron.name) as Neuron;
// Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector); Vector3 worldVector = this.gameObject.transform.TransformVector(siblingNeuron.outputValue);
// } Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector);
// } }
// } // if (this.currentNucleus is Cluster cluster) {
// else { // foreach (Cluster sibling in cluster.siblingClusters) {
if (this.currentNucleus is Neuron currentNeuron) {
Vector3 worldVector = this.gameObject.transform.TransformVector(currentNeuron.outputValue); // }
Handles.color = Color.yellow; // }
Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector); // // if (this.currentNucleus is IReceptor receptor) {
// // foreach (Nucleus nucleus in receptor.nucleiArray) {
// // if (nucleus is Neuron neuron) {
// // Vector3 worldVector = this.gameObject.transform.TransformVector(neuron.outputValue);
// // Handles.color = Color.yellow;
// // Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector);
// // }
// // }
}
else {
if (this.currentNucleus is Neuron currentNeuron) {
Vector3 worldVector = this.gameObject.transform.TransformVector(currentNeuron.outputValue);
Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector);
}
} }
// }
} }
} }

View File

@ -111,13 +111,13 @@ namespace NanoBrain {
Neuron synapseNeuron = prefabSynapse.neuron; Neuron synapseNeuron = prefabSynapse.neuron;
if (synapseNeuron.parent.prefab != null && synapseNeuron.parent.prefab != this.prefab) { if (synapseNeuron.parent.prefab != null && synapseNeuron.parent.prefab != 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.parent.prefab; Cluster prefabCluster = synapseNeuron.parent;
Cluster clonedCluster = this.nuclei.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.cluster.nuclei, prefabSynapse.neuron.name); int neuronIx = GetNucleusIndex(prefabCluster.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;
@ -140,28 +140,6 @@ namespace NanoBrain {
// Debug.Log($"Add synapse {clonedSender.name} -> {clonedNeuron.name}"); // Debug.Log($"Add synapse {clonedSender.name} -> {clonedNeuron.name}");
} }
} }
// // Copy the receivers, which will also create the synapses
// foreach (Nucleus receiver in prefabNeuron.receivers.ToArray()) {
// int ix = GetNucleusIndex(prefabNuclei, receiver);
// if (ix < 0)
// continue;
// if (clonedNuclei[ix] is not Nucleus clonedReceiver)
// continue;
// // Find the synapse for the weight
// float weight = 1;
// foreach (Synapse synapse in receiver.synapses) {
// // Find the weight for this synapse
// if (synapse.neuron == prefabNucleus) {
// weight = synapse.weight;
// break;
// }
// }
// clonedNeuron.AddReceiver(clonedReceiver, weight);
// }
} }
if (Application.isPlaying) { if (Application.isPlaying) {
@ -191,135 +169,43 @@ namespace NanoBrain {
// Ensure that all neurons are computed to initialize bias // Ensure that all neurons are computed to initialize bias
foreach (Nucleus clonedNucleus in clonedNuclei) { foreach (Nucleus clonedNucleus in clonedNuclei) {
clonedNucleus.UpdateStateIsolated(); if (clonedNucleus is not Cluster)
clonedNucleus.UpdateStateIsolated();
} }
} }
/*
for (int nucleusIx = 0; nucleusIx < clonedNuclei.Length; nucleusIx++) {
Nucleus prefabNucleus = prefabNuclei[nucleusIx];
if (prefabNucleus is not Cluster prefabCluster)
continue;
if (prefabCluster.instanceCount <= 1)
continue;
Cluster clonedNucleus = clonedNuclei[nucleusIx] as Cluster;
if (prefabCluster == prefabCluster.siblingClusters[0]) {
// We clone the array only for the first entry
//NucleusArray clonedArray = new(prefabReceptor.nucleiArray.Length);
Cluster[] clonedArray = new Cluster[prefabCluster.siblingClusters.Length];
int arrayIx = 0;
foreach (Cluster prefabArrayNucleus in prefabCluster.siblingClusters) {
int arrayNucleusIx = GetNucleusIndex(prefabNuclei, prefabArrayNucleus);
if (arrayNucleusIx >= 0) {
Cluster clonedArrayNucleus = clonedNuclei[arrayNucleusIx] as Cluster;
clonedArray[arrayIx] = clonedArrayNucleus;
}
else {
Debug.LogError($" Could not find prefab nucleus {prefabNucleus.name} in the clones");
}
arrayIx++;
}
clonedNucleus.siblingClusters = clonedArray;
}
else {
// The others will refer to the array created for the first nucleus in the array
int firstNucleusIx = GetNucleusIndex(prefabNuclei, prefabCluster.siblingClusters[0]);
Cluster clonedFirstNucleus = clonedNuclei[firstNucleusIx] as Cluster;
clonedNucleus.siblingClusters = clonedFirstNucleus.siblingClusters;
}
}
} }
/* // private void CloneSynapses(Neuron prefabNeuron, Neuron clonedNeuron) {
// Collect the subclusters // foreach (Synapse prefabSynapse in prefabNeuron.synapses) {
List<Cluster> subClusters = new(); // Neuron synapseNeuron = prefabSynapse.neuron;
foreach (Nucleus nucleus in prefabNuclei) { // if (synapseNeuron.parent.prefab != null && synapseNeuron.parent.prefab != this.prefab) {
foreach (Synapse synapse in nucleus.synapses) { // // Neuron is in another cluster, find the cloned cluster first
Nucleus synapseNucleus = synapse.neuron; // ClusterPrefab prefabCluster = synapseNeuron.parent.prefab;
Cluster subCluster = synapseNucleus.parent; // Cluster clonedCluster = this.nuclei.Find(n => n.name == prefabCluster.name) as Cluster;
if (subCluster is null || // if (clonedCluster == null)
synapseNucleus.clusterPrefab == this.clusterPrefab) { // continue;
continue; // // Now find the neuron in that cloned cluster
} // int neuronIx = GetNucleusIndex(prefabCluster.cluster.nuclei, prefabSynapse.neuron.name);
// if (synapseNucleus is not Cluster subCluster) // if (neuronIx < 0)
// continue; // // Could not find the neuron in the prefab cluster
if (subClusters.Contains(subCluster)) // continue;
continue; // if (clonedCluster.nuclei[neuronIx] is not Neuron clonedSender)
subClusters.Add(subCluster); // // Could not find the neuron in the cloned cluster
} // continue;
}
// Create the subcluster instances
foreach (Cluster subCluster in subClusters) {
for (int ix = 0; ix < subCluster.instanceCount; ix++) {
// create the new instance
Cluster clusterInstance = new(subCluster.prefab);
// connect it
foreach ((Neuron sender, Nucleus receiver) in subCluster.CollectConnections()) {
int receiverIx = GetNucleusIndex(prefabNuclei, receiver);
if (receiverIx < 0)
continue;
if (clonedNuclei[receiverIx] is not Nucleus clonedReceiver) // clonedSender.AddReceiver(clonedNeuron, prefabSynapse.weight);
continue; // //Debug.Log($"Add synapse {clonedCluster.name}.{clonedSender.name} -> {clonedNeuron.name} [{clonedSender.receivers.Count}]");
// }
// else {
// Neuron clonedSender = this.nuclei.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}");
// }
// }
// Find the synapse for the weight // }
float weight = 1;
foreach (Synapse synapse in receiver.synapses) {
// Find the weight for this synapse
if (synapse.neuron == sender) {
weight = synapse.weight;
break;
}
}
if (clusterInstance.GetNucleus(sender.name) is not Neuron clonedSender)
continue;
clonedSender.AddReceiver(clonedReceiver, weight);
}
}
}
*/
// foreach (Nucleus nucleus in this.clusterNuclei) {
// if (nucleus is Cluster clonedSubCluster)
// RestoreAllExternalReceivers(clonedSubCluster, this.prefab, this);
// }
}
private void CloneSynapses(Neuron prefabNeuron, Neuron clonedNeuron) {
foreach (Synapse prefabSynapse in prefabNeuron.synapses) {
Neuron synapseNeuron = prefabSynapse.neuron;
if (synapseNeuron.parent.prefab != null && synapseNeuron.parent.prefab != this.prefab) {
// Neuron is in another cluster, find the cloned cluster first
ClusterPrefab prefabCluster = synapseNeuron.parent.prefab;
Cluster clonedCluster = this.nuclei.Find(n => n.name == prefabCluster.name) as Cluster;
if (clonedCluster == null)
continue;
// Now find the neuron in that cloned cluster
int neuronIx = GetNucleusIndex(prefabCluster.cluster.nuclei, prefabSynapse.neuron.name);
if (neuronIx < 0)
// Could not find the neuron in the prefab cluster
continue;
if (clonedCluster.nuclei[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.nuclei.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>
/// Sort the nuclei in a correct evaluation order /// Sort the nuclei in a correct evaluation order
@ -537,43 +423,43 @@ namespace NanoBrain {
} }
} }
public virtual Cluster GetThingCluster() { // public virtual Cluster GetThingCluster() {
Cluster selectedCluster = SelectCluster(); // Cluster selectedCluster = SelectCluster();
return selectedCluster; // return selectedCluster;
} // }
public virtual Cluster GetThingCluster(int thingId, string thingName = null) { // public virtual Cluster GetThingCluster(int thingId, string thingName = null) {
if (thingClusters.TryGetValue(thingId, out Cluster cluster)) // if (thingClusters.TryGetValue(thingId, out Cluster cluster))
return cluster; // return cluster;
Cluster selectedCluster = SelectCluster(); // Cluster selectedCluster = SelectCluster();
selectedCluster.name = baseName + ": " + thingName; // selectedCluster.name = baseName + ": " + thingName;
thingClusters[thingId] = selectedCluster; // thingClusters[thingId] = selectedCluster;
return selectedCluster; // return selectedCluster;
} // }
private Cluster SelectCluster() { // private Cluster SelectCluster() {
if (this.siblingClusters == null) // if (this.siblingClusters == null)
return this; // return this;
// Find a sleeping cluster // // Find a sleeping cluster
// foreach (Cluster cluster in this.siblingClusters) { // // foreach (Cluster cluster in this.siblingClusters) {
// if (cluster.defaultOutput.isSleeping) { // // if (cluster.defaultOutput.isSleeping) {
// RemoveThingCluster(cluster); // // RemoveThingCluster(cluster);
// return cluster; // // return cluster;
// } // // }
// } // // }
// Find longest unused cluster // // Find longest unused cluster
// Note this uses the default output... // // Note this uses the default output...
Cluster unusedCluster = this.siblingClusters[0]; // Cluster unusedCluster = this.siblingClusters[0];
for (int ix = 1; ix < this.siblingClusters.Length; ix++) { // for (int ix = 1; ix < this.siblingClusters.Length; ix++) {
if (this.siblingClusters[ix].defaultOutput.lastUpdate < unusedCluster.defaultOutput.lastUpdate) // if (this.siblingClusters[ix].defaultOutput.lastUpdate < unusedCluster.defaultOutput.lastUpdate)
unusedCluster = this.siblingClusters[ix]; // unusedCluster = this.siblingClusters[ix];
} // }
RemoveThingCluster(unusedCluster); // RemoveThingCluster(unusedCluster);
return unusedCluster; // return unusedCluster;
} // }
private void RemoveThingCluster(Cluster cluster) { private void RemoveThingCluster(Cluster cluster) {
List<int> keysToRemove = new(); List<int> keysToRemove = new();
@ -756,8 +642,8 @@ namespace NanoBrain {
else { else {
string nucleusName0 = nucleusName + ": 0"; string nucleusName0 = nucleusName + ": 0";
foreach (Nucleus nucleus in this.nuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is Cluster) { //IReceptor receptor) { if (nucleus is Cluster) {
if (nucleus.name == nucleusName | nucleus.name == nucleusName0) if (nucleus.name == nucleusName || nucleus.name == nucleusName0)
return nucleus; return nucleus;
} }
else if (nucleus.name == nucleusName) else if (nucleus.name == nucleusName)
@ -775,6 +661,51 @@ namespace NanoBrain {
return null; return null;
} }
public Neuron GetNeuron(int thingId, string neuronName, string thingName = null) {
if (this.siblingClusters == null || this.siblingClusters.Length <= 1)
return this.GetNeuron(neuronName);
// See if we are already using a cluster for thingId
if (thingClusters.TryGetValue(thingId, out Cluster cluster))
return cluster.GetNeuron(neuronName);
// Find the cluster with the lowest value neuron
Neuron lowestNeuron = null;
foreach (Cluster sibling in this.siblingClusters) {
Neuron neuron = sibling.GetNeuron(neuronName);
if (lowestNeuron == null || neuron.outputMagnitude < lowestNeuron.outputMagnitude)
lowestNeuron = neuron;
}
Cluster selectedCluster = lowestNeuron.parent;
RemoveThingCluster(selectedCluster);
selectedCluster.name = baseName + ": " + thingName;
thingClusters[thingId] = selectedCluster;
return lowestNeuron;
/*
// Find a sleeping cluster
// foreach (Cluster cluster in this.siblingClusters) {
// if (cluster.defaultOutput.isSleeping) {
// RemoveThingCluster(cluster);
// return cluster;
// }
// }
// Find longest unused cluster
// Note this uses the default output...
Cluster unusedCluster = this.siblingClusters[0];
for (int ix = 1; ix < this.siblingClusters.Length; ix++) {
if (this.siblingClusters[ix].defaultOutput.lastUpdate < unusedCluster.defaultOutput.lastUpdate)
unusedCluster = this.siblingClusters[ix];
}
RemoveThingCluster(unusedCluster);
//return unusedCluster;
Cluster cluster = GetThingCluster(thingId, thingName);
Neuron neuron = cluster?.GetNeuron(neuronName);
return neuron;
*/
}
public bool DeleteNucleus(Nucleus nucleus) { public bool DeleteNucleus(Nucleus nucleus) {
if (this.nuclei.Contains(nucleus) == false) { if (this.nuclei.Contains(nucleus) == false) {
// Try to find the nucleus by name // Try to find the nucleus by name
@ -883,27 +814,19 @@ namespace NanoBrain {
} }
List<Nucleus> computeOrder = this.computeOrders[startNucleus]; List<Nucleus> computeOrder = this.computeOrders[startNucleus];
//if (startNucleus.trace)
// 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)
// Debug.Log($" {nucleus.name}");
if (nucleus is Neuron neuron) { if (nucleus is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) { foreach (Nucleus receiver in neuron.receivers) {
if (receiver.parent != this) { if (receiver.parent != this) {
Debug.Log($" External: {receiver.parent.name}.{receiver.name}"); //Debug.Log($" External: {receiver.parent.name}.{receiver.name}");
receiver.parent.UpdateFromNucleus(receiver); receiver.parent.UpdateFromNucleus(receiver);
} }
} }
} }
} }
} }
// continue in parent
//this.parent?.UpdateFromNucleus(this);
UpdateNuclei(); UpdateNuclei();
} }
@ -911,6 +834,7 @@ namespace NanoBrain {
throw new Exception("Cluster should not be updated!"); throw new Exception("Cluster should not be updated!");
} }
// Don't think this does anything anymore...
public override void UpdateNuclei() { public override void UpdateNuclei() {
foreach (Nucleus nucleus in this.nuclei) foreach (Nucleus nucleus in this.nuclei)
nucleus.UpdateNuclei(); nucleus.UpdateNuclei();
@ -921,9 +845,9 @@ namespace NanoBrain {
public void Refresh() { public void Refresh() {
// This should not be needed, but somehow somewhere the parent is changed... // This should not be needed, but somehow somewhere the parent is changed...
foreach (Nucleus nucleus in this.nuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is not Neuron neuron) // if (nucleus is not Neuron neuron)
continue; // continue;
neuron.parent = this; nucleus.parent = this;
} }
RefreshOutputs(); RefreshOutputs();
RefreshComputeOrders(); RefreshComputeOrders();

View File

@ -276,16 +276,11 @@ namespace NanoBrain {
public float outputSqrMagnitude => _outputValue.sqrMagnitude; public float outputSqrMagnitude => _outputValue.sqrMagnitude;
#endif #endif
public bool isFiring { public bool isFiring => this.outputMagnitude > 0.5f;
get {
//SleepCheck();
return this.outputMagnitude > 0.5f;
}
}
public Action WhenFiring; public Action WhenFiring;
public bool persistOutput = false; public bool persistOutput = false;
public virtual bool isSleeping => persistOutput ? false : (Time.time - this.lastUpdate > this.timeToSleep); public virtual bool isSleeping => !persistOutput && (Time.time - this.lastUpdate > this.timeToSleep);
public void SleepCheck() { public void SleepCheck() {
if (this.isSleeping) { if (this.isSleeping) {
#if UNITY_MATHEMATICS #if UNITY_MATHEMATICS
@ -299,9 +294,9 @@ namespace NanoBrain {
/// <summary> /// <summary>
/// Toggle for printing debugging trace data /// Toggle for printing debugging trace data
/// </summary> /// </summary>
public bool trace = false; //public bool trace = false;
[NonSerialized] //[NonSerialized]
public float lastUpdate = 0; public float lastUpdate = 0;
public readonly float timeToSleep = 1f; public readonly float timeToSleep = 1f;
@ -624,7 +619,13 @@ namespace NanoBrain {
#endregion Receivers #endregion Receivers
public override void ProcessStimulus(Vector3 inputValue) { /// <summary>
/// Process an external stimulus
/// </summary>
/// <param name="inputValue">The value of the stimulus</param>
/// <param name="thingId">The id of the thing causing the stimulus</param>
/// <param name="thingName">The name of the thing causing the stimulus</param>
public virtual void ProcessStimulus(Vector3 inputValue) {
this.lastUpdate = Time.time; this.lastUpdate = Time.time;
this.bias = inputValue; this.bias = inputValue;
this.parent?.UpdateFromNucleus(this); this.parent?.UpdateFromNucleus(this);

View File

@ -142,15 +142,6 @@ public abstract class Nucleus {
// this.parent.UpdateFromNucleus(this); // this.parent.UpdateFromNucleus(this);
// } // }
/// <summary>
/// Process an external stimulus
/// </summary>
/// <param name="inputValue">The value of the stimulus</param>
/// <param name="thingId">The id of the thing causing the stimulus</param>
/// <param name="thingName">The name of the thing causing the stimulus</param>
public virtual void ProcessStimulus(Vector3 inputValue) { //, int thingId = 0, string thingName = "") {
}
#endregion Update #endregion Update
} }