cluster no longer have receivers

This commit is contained in:
Pascal Serrarens 2026-02-17 12:31:39 +01:00
parent a394f582cf
commit 36e73081f9
13 changed files with 289 additions and 199 deletions

View File

@ -15,11 +15,11 @@ public class Cluster : Nucleus {
this.name = prefab.name;
this.parent = parent;
this.parent?.nuclei.Add(this);
this.parent?.clusterNuclei.Add(this);
ClonePrefab();
_ = this.inputs;
this.sortedNuclei = TopologicalSort(this.nuclei);
this.sortedNuclei = TopologicalSort(this.clusterNuclei);
}
public Cluster(ClusterPrefab prefab, ClusterPrefab parent = null) {
@ -32,7 +32,7 @@ public class Cluster : Nucleus {
ClonePrefab();
_ = this.inputs;
this.sortedNuclei = TopologicalSort(this.nuclei);
this.sortedNuclei = TopologicalSort(this.clusterNuclei);
}
private void ClonePrefab() {
@ -40,7 +40,7 @@ public class Cluster : Nucleus {
// first clone the nuclei without their connections
foreach (Nucleus nucleus in this.prefab.nuclei)
nucleus.ShallowCloneTo(this);
Nucleus[] clonedNuclei = this.nuclei.ToArray();
Nucleus[] clonedNuclei = this.clusterNuclei.ToArray();
// Now clone the connections
for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) {
@ -50,26 +50,27 @@ public class Cluster : Nucleus {
continue;
// Copy the receivers, which will also create the synapses
foreach (Nucleus receiver in prefabNucleus.receivers) {
int ix = GetNucleusIndex(prefabNuclei, receiver);
if (ix < 0)
continue;
// Clusters do not have receivers...
// foreach (Nucleus receiver in prefabNucleus.receivers) {
// int ix = GetNucleusIndex(prefabNuclei, receiver);
// if (ix < 0)
// continue;
if (clonedNuclei[ix] is not Nucleus clonedReceiver)
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.nucleus == prefabNucleus) {
weight = synapse.weight;
break;
}
}
// // Find the synapse for the weight
// float weight = 1;
// foreach (Synapse synapse in receiver.synapses) {
// // Find the weight for this synapse
// if (synapse.nucleus == prefabNucleus) {
// weight = synapse.weight;
// break;
// }
// }
clonedReceptor.AddReceiver(clonedReceiver, weight);
}
// clonedReceptor.AddReceiver(clonedReceiver, weight);
// }
}
// Copy nucleus arrays
@ -116,8 +117,10 @@ public class Cluster : Nucleus {
// Calculate in-degrees
foreach (Nucleus node in nodes) {
foreach (Nucleus receiver in node.receivers)
inDegree[receiver]++;
if (node is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers)
inDegree[receiver]++;
}
}
Queue<Nucleus> queue = new();
@ -132,10 +135,12 @@ public class Cluster : Nucleus {
Nucleus current = queue.Dequeue();
sortedOrder.Add(current); // Process the node
foreach (Nucleus receiver in current.receivers) {
inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver);
if (current is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) {
inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver);
}
}
}
@ -153,9 +158,9 @@ public class Cluster : Nucleus {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (Nucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
// foreach (Nucleus receiver in this.receivers) {
// clone.AddReceiver(receiver);
// }
return clone;
}
@ -181,7 +186,7 @@ public class Cluster : Nucleus {
[SerializeReference]
public List<Nucleus> nuclei = new();
public List<Nucleus> clusterNuclei = new();
// the nuclei sorted using topological sorting
// to ensure that the cluster is computer in the right order
public List<Nucleus> sortedNuclei;
@ -192,7 +197,7 @@ public class Cluster : Nucleus {
get {
if (this._inputs == null) {
this._inputs = new();
foreach (Nucleus nucleus in this.nuclei) {
foreach (Nucleus nucleus in this.clusterNuclei) {
// inputs have no synapses
if (nucleus.synapses.Count == 0)
this._inputs.Add(nucleus);
@ -215,7 +220,7 @@ public class Cluster : Nucleus {
HashSet<Nucleus> visited = new HashSet<Nucleus>();
// Initialize in-degrees and mark all nodes as unvisited
foreach (Nucleus node in this.nuclei) {
foreach (Nucleus node in this.clusterNuclei) {
inDegree[node] = 0;
}
@ -226,12 +231,14 @@ public class Cluster : Nucleus {
while (queue.Count > 0) {
Nucleus current = queue.Dequeue();
foreach (Nucleus receiver in current.receivers) {
if (!visited.Contains(receiver)) {
visited.Add(receiver);
queue.Enqueue(receiver);
if (current is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) {
if (!visited.Contains(receiver)) {
visited.Add(receiver);
queue.Enqueue(receiver);
}
inDegree[receiver]++;
}
inDegree[receiver]++;
}
}
@ -248,11 +255,13 @@ public class Cluster : Nucleus {
Nucleus current = queue.Dequeue();
sortedOrder.Add(current); // Process the node
foreach (Nucleus receiver in current.receivers) {
if (visited.Contains(receiver)) {
inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver);
if (current is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) {
if (visited.Contains(receiver)) {
inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver);
}
}
}
}
@ -266,13 +275,15 @@ public class Cluster : Nucleus {
private List<Nucleus> TopologicalSort3(Nucleus startNode) {
Dictionary<Nucleus, int> inDegree = new();
foreach (Nucleus node in this.nuclei)
foreach (Nucleus node in this.clusterNuclei)
inDegree[node] = 0; // Initialize in-degree to zero
// Calculate in-degrees
foreach (Nucleus node in this.nuclei) {
foreach (Nucleus receiver in node.receivers)
inDegree[receiver]++;
foreach (Nucleus node in this.clusterNuclei) {
if (node is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers)
inDegree[receiver]++;
}
}
Queue<Nucleus> queue = new();
@ -283,10 +294,12 @@ public class Cluster : Nucleus {
Nucleus current = queue.Dequeue();
sortedOrder.Add(current); // Process the node
foreach (Nucleus receiver in current.receivers) {
inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver);
if (current is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) {
inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver);
}
}
}
@ -298,22 +311,21 @@ public class Cluster : Nucleus {
return sortedOrder;
}
public virtual Nucleus output {//=> this.nuclei[0] as Nucleus;
public virtual Neuron defaultOutput {//=> this.nuclei[0] as Nucleus;
get {
if (this.nuclei.Count > 0)
return this.nuclei[0];
if (this.clusterNuclei.Count > 0)
return this.clusterNuclei[0] as Neuron;
return null;
}
}
public List<Nucleus> _outputs = null;
public List<Nucleus> outputs {
private List<Neuron> _outputs = null;
public List<Neuron> outputs {
get {
if (this._outputs == null) {
this._outputs = new();
foreach (Nucleus nucleus in this.nuclei) {
// outputs have not receivers
if (nucleus.receivers.Count == 0)
this._outputs.Add(nucleus);
foreach (Nucleus nucleus in this.clusterNuclei) {
if (nucleus is Neuron neuron) // && neuron.receivers.Count == 0)
this._outputs.Add(neuron);
}
}
return this._outputs;
@ -321,7 +333,7 @@ public class Cluster : Nucleus {
}
public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) {
foreach (Nucleus receptor in this.nuclei) {
foreach (Nucleus receptor in this.clusterNuclei) {
if (receptor is Nucleus nucleus)
if (nucleus.name == nucleusName) {
foundNucleus = nucleus;
@ -333,7 +345,7 @@ public class Cluster : Nucleus {
}
public Nucleus GetNucleus(string nucleusName) {
foreach (Nucleus nucleus in this.nuclei) {
foreach (Nucleus nucleus in this.clusterNuclei) {
if (nucleus.name == nucleusName)
return nucleus;
}
@ -341,7 +353,7 @@ public class Cluster : Nucleus {
}
public Receptor GetReceptor(string receptorName) {
foreach (Nucleus nucleus in this.nuclei) {
foreach (Nucleus nucleus in this.clusterNuclei) {
if (nucleus is Receptor receptor)
if (receptor.name == receptorName)
return receptor;
@ -349,6 +361,18 @@ public class Cluster : Nucleus {
return null;
}
#region Receivers
public virtual List<Nucleus> CollectReceivers() {
List<Nucleus> receivers = new();
foreach (Neuron output in this.outputs) {
receivers.AddRange(output.receivers);
}
return receivers;
}
#endregion Receivers
#region Update
public void UpdateFromNucleus(Nucleus startNucleus) {
@ -363,7 +387,7 @@ public class Cluster : Nucleus {
Debug.Log($" {nucleus.name} = {nucleus.outputValue}");
}
this.outputValue = this.output.outputValue;
this.outputValue = this.defaultOutput.outputValue;
this.stale = 0;
UpdateNuclei();
@ -383,14 +407,14 @@ public class Cluster : Nucleus {
foreach (Nucleus nucleus in this.sortedNuclei)
nucleus.UpdateStateIsolated();
this.outputValue = this.output.outputValue;
this.outputValue = this.defaultOutput.outputValue;
this.stale = 0;
UpdateNuclei();
}
public override void UpdateNuclei() {
foreach (Nucleus nucleus in this.nuclei)
foreach (Nucleus nucleus in this.clusterNuclei)
nucleus.UpdateNuclei();
}

View File

@ -38,7 +38,7 @@ public class ClusterPrefab : ScriptableObject {
public void RefreshOutputs() {
this._outputs = new();
foreach (Nucleus nucleus in this.nuclei) {
if (nucleus.receivers.Count == 0)
if (nucleus is Neuron neuron && neuron.receivers.Count == 0)
this._outputs.Add(nucleus);
}
}
@ -85,15 +85,15 @@ public class ClusterPrefab : ScriptableObject {
}
nucleus.synapses.RemoveAll(synapse => visitedSynapses.Contains(synapse) == false);
}
if (nucleus.receivers != null) {
if (nucleus is Neuron neuron && neuron.receivers != null) {
HashSet<Nucleus> visitedReceivers = new();
foreach (Nucleus receiver in nucleus.receivers) {
foreach (Nucleus receiver in neuron.receivers) {
if (receiver != null && receiver != null) {
visitedReceivers.Add(receiver);
visitedNuclei.Add(receiver);
}
}
nucleus.receivers.RemoveAll(receiver => visitedReceivers.Contains(receiver) == false);
neuron.receivers.RemoveAll(receiver => visitedReceivers.Contains(receiver) == false);
}
}

View File

@ -5,7 +5,7 @@ using Unity.Mathematics;
using static Unity.Mathematics.math;
[Serializable]
public class ClusterReceptor : Cluster {
public class ClusterReceptor : Cluster, IReceptor {
public ClusterReceptor(ClusterPrefab prefab, Cluster parent, string name) : base(prefab, parent) {
this.name = name;
this.array ??= new NucleusArray(this);
@ -32,9 +32,9 @@ public class ClusterReceptor : Cluster {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (Nucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
// foreach (Nucleus receiver in this.receivers) {
// clone.AddReceiver(receiver);
// }
return clone;
}
@ -48,21 +48,22 @@ public class ClusterReceptor : Cluster {
#region Receivers
private List<Nucleus> _clusterReceivers = null;
public override List<Nucleus> receivers {
get {
if (_clusterReceivers == null || _clusterReceivers.Count == 0) {
_clusterReceivers = new();
foreach (Nucleus output in this.nuclei) {
_clusterReceivers.AddRange(output.receivers);
}
}
return _clusterReceivers;
}
}
public override void AddReceiver(Nucleus receivingNucleus, float weight = 1) {
this.output.receivers.Add(receivingNucleus);
receivingNucleus.AddSynapse(this.output, weight);
}
// public override List<Nucleus> receivers {
// get {
// if (_clusterReceivers == null || _clusterReceivers.Count == 0) {
// _clusterReceivers = new();
// foreach (Nucleus output in this.clusterNuclei) {
// _clusterReceivers.AddRange(output.receivers);
// }
// }
// return _clusterReceivers;
// }
// }
// public override void AddReceiver(Nucleus receivingNucleus, float weight = 1) {
// string nucleusName = this.
// this.output.receivers.Add(receivingNucleus);
// receivingNucleus.AddSynapse(this.output, weight);
// }
#endregion Receivers
@ -72,7 +73,7 @@ public class ClusterReceptor : Cluster {
foreach (Nucleus nucleus in this.sortedNuclei)
nucleus.UpdateStateIsolated();
this.outputValue = this.output.outputValue;
this.outputValue = this.defaultOutput.outputValue;
this.stale = 0;
UpdateNuclei();
@ -85,7 +86,7 @@ public class ClusterReceptor : Cluster {
this.parent.UpdateFromNucleus(this);
}
foreach (Nucleus nucleus in this.nuclei)
foreach (Nucleus nucleus in this.clusterNuclei)
nucleus.UpdateNuclei();
}

View File

@ -74,9 +74,11 @@ public class BrainEditorWindow : EditorWindow {
int ix = 0;
foreach (Nucleus nucleus in prefab.nuclei) {
nodes.Add(new DagNode() { id = ix, title = nucleus.name });
foreach (Nucleus receiver in nucleus.receivers) {
int receiverIx = prefab.GetNucleusIndex(receiver);
edges.Add(new DagEdge() { fromId = ix, toId = receiverIx });
if (nucleus is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) {
int receiverIx = prefab.GetNucleusIndex(receiver);
edges.Add(new DagEdge() { fromId = ix, toId = receiverIx });
}
}
ix++;
}
@ -191,9 +193,9 @@ public class BrainEditorWindow : EditorWindow {
Handles.DrawSolidDisc(n.position, Vector3.forward, n.radius);
if (GetIncomingEdges(n).Count == 0)
DrawArrowHead(n.position - new Vector2(n.radius + 10, 0), n.position - new Vector2(n.radius + 5, 0), 10f/zoom, 12f/zoom, Color.white);
DrawArrowHead(n.position - new Vector2(n.radius + 10, 0), n.position - new Vector2(n.radius + 5, 0), 10f / zoom, 12f / zoom, Color.white);
if (GetOutgoingEdges(n).Count == 0)
DrawArrowHead(n.position + new Vector2(n.radius + 10, 0), n.position + new Vector2(n.radius + 15, 0), 10f/zoom, 12f/zoom, Color.white);
DrawArrowHead(n.position + new Vector2(n.radius + 10, 0), n.position + new Vector2(n.radius + 15, 0), 10f / zoom, 12f / zoom, Color.white);
Handles.color = Color.white;
GUIStyle style = new(EditorStyles.label) {

View File

@ -195,8 +195,8 @@ public class ClusterInspector : Editor {
return;
NeuroidLayer currentLayer = new() { ix = layerIx };
if (selectedNucleus.receivers != null) {
foreach (Nucleus receiver in selectedNucleus.receivers) {
if (selectedNucleus is Neuron selectedNeuron && selectedNeuron.receivers != null) {
foreach (Nucleus receiver in selectedNeuron.receivers) {
Nucleus outputNeuroid = receiver;
if (outputNeuroid != null) {
AddToLayer(currentLayer, outputNeuroid);
@ -357,12 +357,20 @@ public class ClusterInspector : Editor {
}
private void DrawReceivers(Nucleus nucleus, Vector3 parentPos, float size) {
int nodeCount = nucleus.receivers.Count();
List<Nucleus> receivers = null;
if (nucleus is Neuron neuron)
receivers = neuron.receivers;
else if (nucleus is Cluster cluster)
receivers = cluster.CollectReceivers();
else
return;
int nodeCount = receivers.Count(); //neuron != null ? neuron.receivers.Count() : 1;
// Determine the maximum value in this layer
// This is used to 'scale' the output value colors of the nuclei
float maxValue = 0;
foreach (Nucleus receiver in nucleus.receivers) {
foreach (Nucleus receiver in receivers) {
if (receiver is Neuron neuroid) {
float value = length(neuroid.outputValue);
if (value > maxValue)
@ -376,7 +384,7 @@ public class ClusterInspector : Editor {
int row = 0;
List<NucleusArray> drawnArrays = new();
foreach (Nucleus receiver in nucleus.receivers) {
foreach (Nucleus receiver in receivers) {
if (receiver is Receptor receptor) {
if (drawnArrays.Contains(receptor.array))
continue;
@ -409,7 +417,8 @@ public class ClusterInspector : Editor {
if (drawnArrays.Contains(receptor.array))
continue;
drawnArrays.Add(receptor.array);
} else if (synapse.nucleus.parent is ClusterReceptor clusterReceptor) {
}
else if (synapse.nucleus.parent is ClusterReceptor clusterReceptor) {
if (drawnArrays.Contains(clusterReceptor.array))
continue;
drawnArrays.Add(clusterReceptor.array);
@ -432,7 +441,8 @@ public class ClusterInspector : Editor {
if (drawnArrays.Contains(neuron.array))
continue;
drawnArrays.Add(neuron.array);
} else if (synapse.nucleus.parent is ClusterReceptor clusterReceptor) {
}
else if (synapse.nucleus.parent is ClusterReceptor clusterReceptor) {
if (drawnArrays.Contains(clusterReceptor.array))
continue;
drawnArrays.Add(clusterReceptor.array);
@ -731,56 +741,61 @@ public class ClusterInspector : Editor {
if (this.currentNucleus.synapses.Count > 0) {
Synapse[] synapses = this.currentNucleus.synapses.ToArray();
foreach (Synapse synapse in synapses) {
if (synapse.nucleus != null) {
if (array != null) {
if (array.nuclei.Contains(synapse.nucleus))
continue;
}
else {
if (synapse.nucleus is Receptor receptor2 && receptor2.array != null && receptor2.array.nuclei.Length > 1)
array = receptor2.array;
}
if (synapse.nucleus == null)
continue;
EditorGUILayout.Space();
if (Application.isPlaying) {
Vector3 value = synapse.nucleus.outputValue * synapse.weight;
GUIContent synapseValueLabel = new(synapse.nucleus.name, synapse.nucleus.outputValue.ToString());
EditorGUILayout.FloatField(synapseValueLabel, length(synapse.nucleus.outputValue));
}
else {
EditorGUILayout.BeginHorizontal();
if (synapse.nucleus.parent != null && synapse.nucleus.parent != this.currentNucleus) {
GUIStyle labelStyle = new(GUI.skin.label);
float labelWidth = labelStyle.CalcSize(new GUIContent($"{synapse.nucleus.clusterPrefab.name}.")).x;
EditorGUILayout.LabelField($"{synapse.nucleus.clusterPrefab.name}", GUILayout.Width(labelWidth));
string[] options = synapse.nucleus.parent.nuclei.Select(n => n.name).ToArray();
int selectedIndex = System.Array.IndexOf(options, synapse.nucleus.name);
int newIndex = EditorGUILayout.Popup(selectedIndex, options);
if (newIndex != selectedIndex) {
ChangeSynapse(synapse, synapse.nucleus.parent.nuclei[newIndex]);
}
}
else
EditorGUILayout.LabelField(synapse.nucleus.name);
if (GUILayout.Button("Disconnect")) {
synapse.nucleus.RemoveReceiver(this.currentNucleus);
this.prefab.GarbageCollection();
anythingChanged = true;
}
EditorGUILayout.EndHorizontal();
}
EditorGUI.indentLevel++;
synapse.weight = EditorGUILayout.FloatField("Weight", synapse.weight);
EditorGUI.indentLevel--;
if (array != null) {
if (array.nuclei.Contains(synapse.nucleus))
continue;
if (array.nuclei.Contains(synapse.nucleus.parent))
continue;
}
else {
if (synapse.nucleus.parent is ClusterReceptor clusterReceptor)
array = clusterReceptor.array;
else if (synapse.nucleus is Receptor receptor2) // && receptor2.array != null && receptor2.array.nuclei.Length > 1)
array = receptor2.array;
}
EditorGUILayout.Space();
if (Application.isPlaying) {
Vector3 value = synapse.nucleus.outputValue * synapse.weight;
GUIContent synapseValueLabel = new(synapse.nucleus.name, synapse.nucleus.outputValue.ToString());
EditorGUILayout.FloatField(synapseValueLabel, length(synapse.nucleus.outputValue));
}
else {
EditorGUILayout.BeginHorizontal();
if (synapse.nucleus.parent != null && synapse.nucleus.parent != this.currentNucleus) {
GUIStyle labelStyle = new(GUI.skin.label);
float labelWidth = labelStyle.CalcSize(new GUIContent($"{synapse.nucleus.clusterPrefab.name}.")).x;
EditorGUILayout.LabelField($"{synapse.nucleus.clusterPrefab.name}", GUILayout.Width(labelWidth));
string[] options = synapse.nucleus.parent.clusterNuclei.Select(n => n.name).ToArray();
int selectedIndex = System.Array.IndexOf(options, synapse.nucleus.name);
int newIndex = EditorGUILayout.Popup(selectedIndex, options);
if (newIndex != selectedIndex && synapse.nucleus.parent.clusterNuclei[newIndex] is Neuron newNeuron)
ChangeSynapse(synapse, newNeuron);
}
else
EditorGUILayout.LabelField(synapse.nucleus.name);
if (GUILayout.Button("Disconnect") && synapse.nucleus is Neuron synapseNeuron) {
synapseNeuron.RemoveReceiver(this.currentNucleus);
this.prefab.GarbageCollection();
anythingChanged = true;
}
EditorGUILayout.EndHorizontal();
}
EditorGUI.indentLevel++;
synapse.weight = EditorGUILayout.FloatField("Weight", synapse.weight);
EditorGUI.indentLevel--;
}
}
}
EditorGUILayout.EndFoldoutHeaderGroup();
}
// Activation
EditorGUILayout.Space();
@ -811,7 +826,7 @@ public class ClusterInspector : Editor {
if (GUILayout.Button("Delete this neuron"))
DeleteNeuron(this.currentNucleus);
DeleteNucleus(this.currentNucleus);
if (this.currentNucleus is Cluster subCluster) {
if (GUILayout.Button("Edit Cluster"))
@ -934,10 +949,10 @@ public class ClusterInspector : Editor {
}
protected virtual void AddReceptorArrayInput(Nucleus nucleus) {
ReceptorArray newReceptor = new(this.prefab, "New Receptor");
newReceptor.AddReceiver(nucleus);
this.currentNucleus = newReceptor;
BuildLayers();
// ReceptorArray newReceptor = new(this.prefab, "New Receptor");
// newReceptor.AddReceiver(nucleus);
// this.currentNucleus = newReceptor;
// BuildLayers();
}
protected virtual void AddClusterReceptorInput(Nucleus nucleus) {
@ -946,12 +961,12 @@ public class ClusterInspector : Editor {
private void OnClusterPicked(Nucleus nucleus, ClusterPrefab prefab) {
Cluster subclusterInstance = new(prefab, this.prefab);
subclusterInstance.AddReceiver(nucleus);
subclusterInstance.defaultOutput.AddReceiver(nucleus);
}
private void OnClusterReceptorPicked(Nucleus nucleus, ClusterPrefab selectedPrefab) {
ClusterReceptor clusterInstance = new(selectedPrefab, this.prefab, "New " + selectedPrefab.name);
clusterInstance.AddReceiver(nucleus);
clusterInstance.defaultOutput.AddReceiver(nucleus);
}
private void EditCluster(Cluster subCluster) {
@ -962,7 +977,7 @@ public class ClusterInspector : Editor {
}
// Connect to another nucleus in the same cluster
protected virtual bool ConnectNucleus(ClusterPrefab cluster, Nucleus nucleus) {
protected virtual bool ConnectNucleus(ClusterPrefab cluster, Nucleus nucleusToConnect) {
if (cluster == null)
return false;
@ -980,19 +995,25 @@ public class ClusterInspector : Editor {
if (selectedIndex < 0)
return false;
Nucleus receptor = nuclei.ElementAt(selectedIndex);
receptor.AddReceiver(this.currentNucleus);
Nucleus nucleus = nuclei.ElementAt(selectedIndex);
if (nucleus is Neuron neuron)
neuron.AddReceiver(this.currentNucleus);
else if (nucleus is Cluster subCluster)
subCluster.defaultOutput.AddReceiver(this.currentNucleus);
return true;
}
protected virtual void DeleteNeuron(Nucleus nucleus) {
protected virtual void DeleteNucleus(Nucleus nucleus) {
if (nucleus == null)
return;
foreach (Nucleus receiver in nucleus.receivers) {
if (receiver != null) {
this.currentNucleus = receiver;
break;
if (nucleus is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) {
if (receiver != null) {
this.currentNucleus = receiver;
break;
}
}
}
this.prefab.nuclei.Remove(nucleus);
@ -1021,9 +1042,17 @@ public class ClusterInspector : Editor {
return true;
}
protected virtual void ChangeSynapse(Synapse synapse, Nucleus newNucleus) {
synapse.nucleus.RemoveReceiver(this.currentNucleus);
newNucleus.AddReceiver(this.currentNucleus);
protected virtual void ChangeSynapse(Synapse synapse, Neuron newNucleus) {
Neuron synapseNeuron = synapse.nucleus as Neuron;
if (synapse.nucleus.parent is Cluster subCluster && subCluster.prefab != this.prefab) {
// it is a neuron in a subcluster
synapseNeuron.RemoveReceiver(this.currentNucleus);
newNucleus.AddReceiver(this.currentNucleus);
}
else {
synapseNeuron.RemoveReceiver(this.currentNucleus);
newNucleus.AddReceiver(this.currentNucleus);
}
}
protected virtual void DisconnectNucleus(Neuron nucleus) {
@ -1032,10 +1061,10 @@ public class ClusterInspector : Editor {
string[] names = this.currentNucleus.synapses.Select(synapse => synapse.nucleus.name).ToArray();
int selectedIndex = -1;
selectedIndex = EditorGUILayout.Popup("Disconnect from", selectedIndex, names);
//if (selectedIndex >= 0 && selectedIndex < this.currentNucleus.brain.perceptei.Count) {
if (selectedIndex >= 0 && selectedIndex < this.currentNucleus.clusterPrefab.nuclei.Count) {
Synapse synapse = this.currentNucleus.synapses[selectedIndex];
synapse.nucleus.RemoveReceiver(this.currentNucleus);
Neuron synapseNeuron = synapse.nucleus as Neuron;
synapseNeuron.RemoveReceiver(this.currentNucleus);
}
}

View File

@ -36,7 +36,7 @@ public class NanoBrainComponent_Editor : Editor {
root.Add(brainField);
}
ClusterInspector.CreateInspector(root, brain.prefab, brain.output, component.gameObject);
ClusterInspector.CreateInspector(root, brain.prefab, brain.defaultOutput, component.gameObject);
if (Application.isPlaying == false)
serializedObject.ApplyModifiedProperties();

5
IReceptor.cs Normal file
View File

@ -0,0 +1,5 @@
public interface IReceptor {
public NucleusArray array {
get; set;
}
}

2
IReceptor.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 73f052292ad16bb53a3c07aa1694c705

View File

@ -18,7 +18,7 @@ public class NanoBrain : MonoBehaviour {
}
public static void UpdateWeight(Cluster brain, string name, float weight) {
Nucleus root = brain.output;
Nucleus root = brain.defaultOutput;
foreach (Synapse synapse in root.synapses) {
if (synapse.nucleus.name == name) {
if (synapse.weight != weight) {

View File

@ -12,7 +12,7 @@ public class Neuron : Nucleus {
public Neuron(Cluster parent, string name) {
this.parent = parent;
this.name = name;
this.parent?.nuclei.Add(this);
this.parent?.clusterNuclei.Add(this);
}
public Neuron(ClusterPrefab prefab, string name) {
this.clusterPrefab = prefab;
@ -25,7 +25,6 @@ public class Neuron : Nucleus {
#region Serialization
//public Type type = Type.Neuron;
public enum CombinatorType {
Sum,
Product,
@ -160,11 +159,14 @@ public class Neuron : Nucleus {
}
}
}
foreach (Nucleus receiver in nucleus.receivers) {
if (receiver != null && receiver.synapses != null)
receiver.synapses.RemoveAll(s => s.nucleus == nucleus);
if (nucleus is Neuron neuron) {
foreach (Nucleus receiver in neuron.receivers) {
if (receiver != null && receiver.synapses != null)
receiver.synapses.RemoveAll(s => s.nucleus == nucleus);
}
}
if (nucleus.clusterPrefab != null) {
nucleus.clusterPrefab.nuclei.RemoveAll(n => n == nucleus);
nucleus.clusterPrefab.GarbageCollection();
@ -259,6 +261,48 @@ public class Neuron : Nucleus {
#endregion Activator
#region Receivers
[SerializeReference]
private List<Nucleus> _receivers = new();
public virtual List<Nucleus> receivers {
get { return _receivers; }
set { _receivers = value; }
}
public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) {
if (this is IReceptor receptor) {
foreach (Nucleus element in receptor.array.nuclei) {
if (element is Neuron neuron) {
neuron._receivers.Add(receiverToAdd);
receiverToAdd.AddSynapse(element, weight);
}
}
}
else {
this._receivers.Add(receiverToAdd);
receiverToAdd.AddSynapse(this, weight);
}
}
public virtual void RemoveReceiver(Nucleus receiverToRemove) {
if (this is IReceptor receptor) {
foreach (Nucleus element in receptor.array.nuclei) {
if (element is Neuron neuron) {
neuron._receivers.RemoveAll(receiver => receiver == receiverToRemove);
receiverToRemove.synapses.RemoveAll(synapse => synapse.nucleus == neuron);
}
}
}
else {
this._receivers.RemoveAll(receiver => receiver == receiverToRemove);
receiverToRemove.synapses.RemoveAll(synapse => synapse.nucleus == this);
}
}
#endregion Receivers
public virtual void ProcessStimulus(Vector3 inputValue, string thingName = null) {
this.stale = 0;
this.bias = inputValue;

View File

@ -17,7 +17,6 @@ public abstract class Nucleus {
public virtual float3 outputValue {
get { return _outputValue; }
set {
//this.stale = 0;
_outputValue = value;
if (this.isFiring)
WhenFiring?.Invoke();
@ -68,29 +67,13 @@ public abstract class Nucleus {
return null;
}
public void RemoveSynapse(Nucleus sendingNucleus) {
this.synapses.RemoveAll(synapse => synapse.nucleus == sendingNucleus);
}
#endregion Synapses
#region Receivers
[SerializeReference]
private List<Nucleus> _receivers = new();
public virtual List<Nucleus> receivers {
get { return _receivers; }
set { _receivers = value; }
}
public virtual void AddReceiver(Nucleus receivingNucleus, float weight = 1) {
this._receivers.Add(receivingNucleus);
receivingNucleus.AddSynapse(this, weight);
}
public void RemoveReceiver(Nucleus receiverNucleus) {
this._receivers.RemoveAll(receiver => receiver == receiverNucleus);
receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
}
#endregion Receivers
#region Update

View File

@ -3,7 +3,7 @@ using Unity.Mathematics;
using static Unity.Mathematics.math;
[System.Serializable]
public class Receptor : Neuron {
public class Receptor : Neuron, IReceptor {
public Receptor(Cluster parent, string name) : base(parent, name) { }
public Receptor(ClusterPrefab prefab, string name) : base(prefab, name) { }

View File

@ -46,7 +46,7 @@ public class ReceptorArray : Nucleus {
this._instances[0] = new ReceptorInstance(parent, this.name + "[0]") {
receptor = this
};
this.parent?.nuclei.Add(this);
this.parent?.clusterNuclei.Add(this);
}
public ReceptorArray(ClusterPrefab prefab, string name) {
this.clusterPrefab = prefab;
@ -89,9 +89,9 @@ public class ReceptorArray : Nucleus {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (Nucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
// foreach (Nucleus receiver in this.receivers) {
// clone.AddReceiver(receiver);
// }
return clone;
}