Removed the use of INucleus

This commit is contained in:
Pascal Serrarens 2026-02-05 17:32:01 +01:00
parent 351f242f2e
commit 1e5a19c1ab
13 changed files with 148 additions and 451 deletions

View File

@ -42,26 +42,26 @@ public class Cluster : Nucleus {
}
private void ClonePrefab() {
INucleus[] prefabNuclei = this.prefab.nuclei.ToArray();
Nucleus[] prefabNuclei = this.prefab.nuclei.ToArray();
// first clone the nuclei without their connections
foreach (INucleus nucleus in this.prefab.nuclei)
foreach (Nucleus nucleus in this.prefab.nuclei)
nucleus.ShallowCloneTo(this);
INucleus[] clonedNuclei = this.nuclei.ToArray();
Nucleus[] clonedNuclei = this.nuclei.ToArray();
// Now clone the connections
for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) {
INucleus receptor = prefabNuclei[nucleusIx];
INucleus clonedReceptor = clonedNuclei[nucleusIx];
Nucleus receptor = prefabNuclei[nucleusIx];
Nucleus clonedReceptor = clonedNuclei[nucleusIx];
if (clonedReceptor == null)
continue;
// Copy the receivers, which will also create the synapses
foreach (INucleus receiver in receptor.receivers) {
foreach (Nucleus receiver in receptor.receivers) {
int ix = GetNucleusIndex(prefabNuclei, receiver);
if (ix < 0)
continue;
if (clonedNuclei[ix] is not INucleus clonedReceiver)
if (clonedNuclei[ix] is not Nucleus clonedReceiver)
continue;
// Find the synapse for the weight
@ -80,21 +80,21 @@ public class Cluster : Nucleus {
// Copy nucleus arrays
for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) {
INucleus prefabReceptor = prefabNuclei[nucleusIx];
if (prefabReceptor is not INucleus prefabNucleus)
Nucleus prefabReceptor = prefabNuclei[nucleusIx];
if (prefabReceptor is not Nucleus prefabNucleus)
continue;
if (prefabNucleus.array == null || prefabNucleus.array.nuclei == null || prefabNucleus.array.nuclei.Length == 0)
continue;
INucleus clonedNucleus = clonedNuclei[nucleusIx] as INucleus;
Nucleus clonedNucleus = clonedNuclei[nucleusIx] as Nucleus;
if (prefabNucleus == prefabNucleus.array.nuclei[0]) {
// We clone the array only for the first entry
NucleusArray clonedArray = new(prefabNucleus.array.nuclei.Length, "array");
int arrayIx = 0;
foreach (INucleus prefabArrayNucleus in prefabNucleus.array.nuclei) {
foreach (Nucleus prefabArrayNucleus in prefabNucleus.array.nuclei) {
int arrayNucleusIx = GetNucleusIndex(prefabNuclei, prefabArrayNucleus);
INucleus clonedArrayNucleus = clonedNuclei[arrayNucleusIx];
Nucleus clonedArrayNucleus = clonedNuclei[arrayNucleusIx];
clonedArray.nuclei[arrayIx] = clonedArrayNucleus;
arrayIx++;
}
@ -103,36 +103,36 @@ public class Cluster : Nucleus {
else {
// The others will refer to the array created for the first nucleus in the array
int firstNucleusIx = GetNucleusIndex(prefabNuclei, prefabNucleus.array.nuclei[0]);
INucleus clonedFirstNucleus = clonedNuclei[firstNucleusIx] as INucleus;
Nucleus clonedFirstNucleus = clonedNuclei[firstNucleusIx] as Nucleus;
clonedNucleus.array = clonedFirstNucleus.array;
}
}
}
// Sort the nuclei in a correct evaluation order
private List<INucleus> TopologicalSort(List<INucleus> nodes) {
Dictionary<INucleus, int> inDegree = new();
foreach (INucleus node in nodes)
private List<Nucleus> TopologicalSort(List<Nucleus> nodes) {
Dictionary<Nucleus, int> inDegree = new();
foreach (Nucleus node in nodes)
inDegree[node] = 0; // Initialize in-degree to zero
// Calculate in-degrees
foreach (INucleus node in nodes) {
foreach (INucleus receiver in node.receivers)
foreach (Nucleus node in nodes) {
foreach (Nucleus receiver in node.receivers)
inDegree[receiver]++;
}
Queue<INucleus> queue = new();
foreach (INucleus node in nodes) {
Queue<Nucleus> queue = new();
foreach (Nucleus node in nodes) {
if (inDegree[node] == 0) // Nodes with no dependencies
queue.Enqueue(node);
}
List<INucleus> sortedOrder = new();
List<Nucleus> sortedOrder = new();
while (queue.Count > 0) {
INucleus current = queue.Dequeue();
Nucleus current = queue.Dequeue();
sortedOrder.Add(current); // Process the node
foreach (INucleus receiver in current.receivers) {
foreach (Nucleus receiver in current.receivers) {
inDegree[receiver]--;
if (inDegree[receiver] == 0) // If all dependencies resolved
queue.Enqueue(receiver);
@ -146,7 +146,7 @@ public class Cluster : Nucleus {
return sortedOrder;
}
public override INucleus Clone() {
public override Nucleus Clone() {
//Neuron clone = new(this.cluster, this.name) {
Neuron clone = new(this.parent, this.name) {
array = this.array,
@ -156,20 +156,20 @@ public class Cluster : Nucleus {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (INucleus receiver in this.receivers) {
foreach (Nucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
return clone;
}
public override INucleus ShallowCloneTo(Cluster parent) {
public override Nucleus ShallowCloneTo(Cluster parent) {
Cluster clone = new(this.prefab, parent) {
name = this.name,
};
return clone;
}
private int GetNucleusIndex(INucleus[] nucleiArray, INucleus nucleus) {
private int GetNucleusIndex(Nucleus[] nucleiArray, Nucleus nucleus) {
for (int i = 0; i < nucleiArray.Length; i++) {
if (nucleus == nucleiArray[i])
return i;
@ -185,18 +185,18 @@ public class Cluster : Nucleus {
// public Cluster parent { get; set; }
[SerializeReference]
public List<INucleus> nuclei = new();
public List<Nucleus> nuclei = new();
// the nuclei sorted using topological sorting
// to ensure that the cluster is computer in the right order
public List<INucleus> sortedNuclei;
public List<Nucleus> sortedNuclei;
public List<INucleus> _inputs = null;
public virtual List<INucleus> inputs {
public List<Nucleus> _inputs = null;
public virtual List<Nucleus> inputs {
get {
if (this._inputs == null) {
this._inputs = new();
foreach (INucleus receptor in this.nuclei) {
if (receptor is INucleus nucleus) {
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);
@ -207,10 +207,10 @@ public class Cluster : Nucleus {
}
}
public virtual INucleus output {//=> this.nuclei[0] as INucleus;
public virtual Nucleus output {//=> this.nuclei[0] as Nucleus;
get {
if (this.nuclei.Count > 0)
return this.nuclei[0] as INucleus;
return this.nuclei[0];
return null;
}
}
@ -224,7 +224,7 @@ public class Cluster : Nucleus {
// }
public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) {
foreach (INucleus receptor in this.nuclei) {
foreach (Nucleus receptor in this.nuclei) {
if (receptor is Nucleus nucleus)
if (nucleus.name == nucleusName) {
foundNucleus = nucleus;
@ -236,7 +236,7 @@ public class Cluster : Nucleus {
}
public Nucleus GetNucleus(string nucleusName) {
foreach (INucleus receptor in this.nuclei) {
foreach (Nucleus receptor in this.nuclei) {
if (receptor is Nucleus nucleus)
if (nucleus.name == nucleusName)
return nucleus;
@ -250,7 +250,7 @@ public class Cluster : Nucleus {
// private List<Synapse> _synapses = new();
// public List<Synapse> synapses => _synapses;
// public Synapse AddSynapse(INucleus sendingNucleus, float weight = 1.0f) {
// public Synapse AddSynapse(Nucleus sendingNucleus, float weight = 1.0f) {
// Synapse synapse = new(sendingNucleus, weight);
// this._synapses.Add(synapse);
// return synapse;
@ -266,18 +266,18 @@ public class Cluster : Nucleus {
#region Receivers
// [SerializeReference]
// private List<INucleus> _receivers = new();
// public List<INucleus> receivers {
// private List<Nucleus> _receivers = new();
// public List<Nucleus> receivers {
// get { return _receivers; }
// set { _receivers = value; }
// }
// public virtual void AddReceiver(INucleus receivingNucleus, float weight = 1) {
// public virtual void AddReceiver(Nucleus receivingNucleus, float weight = 1) {
// this._receivers.Add(receivingNucleus);
// receivingNucleus.AddSynapse(this, weight);
// }
// public void RemoveReceiver(INucleus receiverNucleus) {
// public void RemoveReceiver(Nucleus receiverNucleus) {
// this._receivers.RemoveAll(receiver => receiver == receiverNucleus);
// receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
// }
@ -317,7 +317,7 @@ public class Cluster : Nucleus {
// //this.inputs[0].UpdateState(sum);
// this.inputs[0].UpdateStateIsolated(sum);
// foreach (IReceptor receptor in this.sortedNuclei) {
// if (receptor is INucleus nucleus && nucleus != this.inputs[0])
// if (receptor is Nucleus nucleus && nucleus != this.inputs[0])
// nucleus.UpdateStateIsolated();
// }
@ -341,8 +341,8 @@ public class Cluster : Nucleus {
//this.inputs[0].UpdateState(sum);
this.inputs[0].UpdateStateIsolated(sum);
foreach (INucleus receptor in this.sortedNuclei) {
if (receptor is INucleus nucleus && nucleus != this.inputs[0]) {
foreach (Nucleus receptor in this.sortedNuclei) {
if (receptor is Nucleus nucleus && nucleus != this.inputs[0]) {
//if (nucleus.isSleeping == false)
nucleus.UpdateStateIsolated();
}
@ -360,7 +360,7 @@ public class Cluster : Nucleus {
// // }
// this.outputValue = result;
// foreach (INucleus receiver in this.receivers)
// foreach (Nucleus receiver in this.receivers)
// receiver.UpdateState();
// }
@ -370,7 +370,7 @@ public class Cluster : Nucleus {
_outputValue = Vector3.zero;
//foreach (IReceptor nucleus in this.prefab.nuclei)
foreach (INucleus nucleus in this.nuclei)
foreach (Nucleus nucleus in this.nuclei)
nucleus.UpdateNuclei();
}

View File

@ -3,22 +3,22 @@ using UnityEngine;
[CreateAssetMenu(menuName = "Passer/Cluster")]
public class ClusterPrefab : ScriptableObject {
// The ScriptableObject asset from which the runtime object has been created
public string hello = "hello";
[SerializeReference]
public List<INucleus> nuclei = new();
public List<Nucleus> nuclei = new();
public virtual INucleus output => this.nuclei[0] as INucleus;
public virtual Nucleus output => this.nuclei[0] as Nucleus;
public List<INucleus> _inputs = null;
public virtual List<INucleus> inputs {
public List<Nucleus> _inputs = null;
public virtual List<Nucleus> inputs {
get {
if (this._inputs == null) {
this._inputs = new();
foreach (INucleus receptor in this.nuclei) {
if (receptor is INucleus nucleus) {
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);
@ -33,19 +33,19 @@ public class ClusterPrefab : ScriptableObject {
// This is an invariant and should be ensured before the nucleus is used
// because output requires it.
public void EnsureInitialization() {
nuclei ??= new List<INucleus>();
nuclei ??= new List<Nucleus>();
if (nuclei.Count == 0)
new Neuron(this, "Output"); // Every cluster should have at least 1 neuron
}
public void GarbageCollection() {
HashSet<INucleus> visitedNuclei = new();
HashSet<Nucleus> visitedNuclei = new();
MarkNuclei(visitedNuclei, this.output);
//Debug.Log($"Garbage collection found {visitedNuclei.Count} Nuclei");
this.nuclei.RemoveAll(nucleus => nucleus is INucleus n && visitedNuclei.Contains(n) == false);
this.nuclei.RemoveAll(nucleus => nucleus is Nucleus n && visitedNuclei.Contains(n) == false);
}
public void MarkNuclei(HashSet<INucleus> visitedNuclei, INucleus nucleus) {
public void MarkNuclei(HashSet<Nucleus> visitedNuclei, Nucleus nucleus) {
if (nucleus is null)
return;
@ -55,15 +55,15 @@ public class ClusterPrefab : ScriptableObject {
foreach (Synapse synapse in nucleus.synapses) {
if (synapse != null && synapse.nucleus != null) {
visitedSynapses.Add(synapse);
if (synapse.nucleus is INucleus synapse_nucleus)
if (synapse.nucleus is Nucleus synapse_nucleus)
MarkNuclei(visitedNuclei, synapse_nucleus);
}
}
nucleus.synapses.RemoveAll(synapse => visitedSynapses.Contains(synapse) == false);
}
if (nucleus.receivers != null) {
HashSet<INucleus> visitedReceivers = new();
foreach (INucleus receiver in nucleus.receivers) {
HashSet<Nucleus> visitedReceivers = new();
foreach (Nucleus receiver in nucleus.receivers) {
if (receiver != null && receiver != null) {
visitedReceivers.Add(receiver);
visitedNuclei.Add(receiver);
@ -74,7 +74,7 @@ public class ClusterPrefab : ScriptableObject {
}
public virtual void UpdateNuclei() {
foreach (INucleus nucleus in this.nuclei)
foreach (Nucleus nucleus in this.nuclei)
nucleus.UpdateNuclei();
}
}

View File

@ -68,10 +68,10 @@ public class ClusterInspector : Editor {
public class GraphView : VisualElement {
ClusterPrefab cluster;
SerializedObject serializedBrain;
INucleus currentNucleus;
Nucleus currentNucleus;
GameObject gameObject;
private List<NeuroidLayer> layers = new();
private readonly Dictionary<INucleus, Vector2Int> neuroidPositions = new();
private readonly Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
private bool expandArray = false;
ClusterWrapper currentWrapper;
@ -108,7 +108,7 @@ public class ClusterInspector : Editor {
subscribed = false;
}
public void SetGraph(GameObject gameObject, ClusterPrefab brain, INucleus nucleus, VisualElement inspectorContainer) {
public void SetGraph(GameObject gameObject, ClusterPrefab brain, Nucleus nucleus, VisualElement inspectorContainer) {
this.gameObject = gameObject;
this.cluster = brain;
if (Application.isPlaying == false)
@ -136,14 +136,14 @@ public class ClusterInspector : Editor {
this.layers = new();
int layerIx = 0;
INucleus selectedNucleus = this.currentNucleus;
Nucleus selectedNucleus = this.currentNucleus;
if (selectedNucleus == null)
return;
NeuroidLayer currentLayer = new() { ix = layerIx };
if (selectedNucleus.receivers != null) {
foreach (INucleus receiver in selectedNucleus.receivers) {
INucleus outputNeuroid = receiver;
foreach (Nucleus receiver in selectedNucleus.receivers) {
Nucleus outputNeuroid = receiver;
if (outputNeuroid != null) {
AddToLayer(currentLayer, outputNeuroid);
// Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
@ -165,7 +165,7 @@ public class ClusterInspector : Editor {
if (selectedNucleus.synapses != null) {
foreach (Synapse synapse in selectedNucleus.synapses) {
INucleus input = synapse.nucleus;
Nucleus input = synapse.nucleus;
AddToLayer(currentLayer, input);
// Debug.Log($"layer {layerIx} nucleus {input.name}");
}
@ -175,7 +175,7 @@ public class ClusterInspector : Editor {
}
}
private void AddToLayer(NeuroidLayer layer, INucleus nucleus) {
private void AddToLayer(NeuroidLayer layer, Nucleus nucleus) {
if (nucleus == null)
return;
layer.neuroids.Add(nucleus);
@ -210,7 +210,7 @@ public class ClusterInspector : Editor {
// Draw selected Nucleus
if (expandArray) {
float maxValue = 0;
foreach (INucleus nucleus in this.currentNucleus.array.nuclei) {
foreach (Nucleus nucleus in this.currentNucleus.array.nuclei) {
float value = length(nucleus.outputValue);
if (value > maxValue)
maxValue = value;
@ -231,7 +231,7 @@ public class ClusterInspector : Editor {
Handles.color = Color.black;
Handles.DrawAAConvexPolygon(verts);
int row = 0;
foreach (INucleus nucleus in this.currentNucleus.array.nuclei) {
foreach (Nucleus nucleus in this.currentNucleus.array.nuclei) {
Vector3 pos = new(150, margin + row * spacing, 0.0f);
Handles.color = Color.white;
// The selected nucleus highlight ring
@ -255,13 +255,13 @@ public class ClusterInspector : Editor {
}
}
private void DrawReceivers(INucleus nucleus, Vector3 parentPos, float size) {
private void DrawReceivers(Nucleus nucleus, Vector3 parentPos, float size) {
int nodeCount = nucleus.receivers.Count();
// Determine the maximum value in this layer
// This is used to 'scale' the output value colors of the nuclei
float maxValue = 0;
foreach (INucleus receiver in nucleus.receivers) {
foreach (Nucleus receiver in nucleus.receivers) {
if (receiver is Neuron neuroid) {
float value = length(neuroid.outputValue);
if (value > maxValue)
@ -275,12 +275,12 @@ public class ClusterInspector : Editor {
int row = 0;
List<NucleusArray> drawnArrays = new();
foreach (INucleus receiver in nucleus.receivers) {
foreach (Nucleus receiver in nucleus.receivers) {
if (drawnArrays.Contains(receiver.array))
continue;
drawnArrays.Add(receiver.array);
INucleus receiverNucleus = receiver;
Nucleus receiverNucleus = receiver;
if (receiverNucleus == null)
continue;
@ -293,7 +293,7 @@ public class ClusterInspector : Editor {
}
}
private void DrawSynapses(INucleus nucleus, Vector3 parentPos, float size) {
private void DrawSynapses(Nucleus nucleus, Vector3 parentPos, float size) {
int nodeCount = nucleus.synapses.Count;
// Determine the maximum value in this layer
@ -344,7 +344,7 @@ public class ClusterInspector : Editor {
}
}
private void DrawNucleus(INucleus nucleus, Vector3 position, float maxValue, float size) {
private void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue, float size) {
Color color;
if (nucleus.isSleeping)
color = Color.darkRed;
@ -359,7 +359,7 @@ public class ClusterInspector : Editor {
DrawNucleus(nucleus, position, maxValue, size, color);
}
private void DrawNucleus(INucleus nucleus, Vector3 position, float maxValue, float size, Color color) {
private void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue, float size, Color color) {
if (nucleus is MemoryCell memory) {
Handles.color = Color.white;
Handles.DrawWireDisc(position + Vector3.right * 10, Vector3.forward, size);
@ -377,7 +377,7 @@ public class ClusterInspector : Editor {
normal = { textColor = Color.white },
fontStyle = FontStyle.Bold,
};
if (nucleus is INucleus neuron) {
if (nucleus is Nucleus neuron) {
if (neuron.array == null || neuron.array.nuclei == null || neuron.array.nuclei.Count() == 0)
neuron.array = new NucleusArray(neuron);
@ -386,7 +386,7 @@ public class ClusterInspector : Editor {
}
if (expandArray && neuron.array.nuclei.First() == this.currentNucleus) {
int arrayIx = 0;
foreach (INucleus n in neuron.array.nuclei) {
foreach (Nucleus n in neuron.array.nuclei) {
if (n == neuron)
break;
arrayIx++;
@ -426,9 +426,9 @@ public class ClusterInspector : Editor {
}
}
private void HandleMouseHover(INucleus nucleus, Rect rect) {
private void HandleMouseHover(Nucleus nucleus, Rect rect) {
GUIContent tooltip;
// if (nucleus is INucleus n) {
// if (nucleus is Nucleus n) {
// tooltip = new(
// $"{nucleus.name}" +
// //$"\nsynapse count {n.synapses.Count}" +
@ -449,14 +449,14 @@ public class ClusterInspector : Editor {
GUI.Box(tooltipRect, tooltip);
}
private void HandleClicked(INucleus nucleus) {
private void HandleClicked(Nucleus nucleus) {
if (nucleus == this.currentNucleus) {
if (nucleus is INucleus n) {
if (nucleus is Nucleus n) {
expandArray = !expandArray;
return;
}
}
else if (nucleus is INucleus n) {
else if (nucleus is Nucleus n) {
this.currentNucleus = n;
BuildLayers();
}
@ -598,7 +598,7 @@ public class ClusterInspector : Editor {
}
}
protected virtual void AddInput(int selectedInputType, INucleus nucleus) {
protected virtual void AddInput(int selectedInputType, Nucleus nucleus) {
switch (selectedInputType) {
case 0: // Neuron
AddInputNeuron(nucleus);
@ -615,7 +615,7 @@ public class ClusterInspector : Editor {
}
}
protected virtual void AddInputNeuron(INucleus nucleus) {
protected virtual void AddInputNeuron(Nucleus nucleus) {
//Neuron newNeuroid = new(this.cluster, "New neuron");
Neuron newNeuroid = new(this.cluster, "New neuron");
newNeuroid.AddReceiver(nucleus);
@ -623,12 +623,12 @@ public class ClusterInspector : Editor {
BuildLayers();
}
protected virtual void DeleteNeuron(INucleus nucleus) {
protected virtual void DeleteNeuron(Nucleus nucleus) {
if (nucleus == null)
return;
if (nucleus.cluster != null)
this.currentNucleus = nucleus.cluster.output;
foreach (INucleus receiver in nucleus.receivers) {
foreach (Nucleus receiver in nucleus.receivers) {
if (receiver != null) {
this.currentNucleus = receiver;
break;
@ -638,25 +638,25 @@ public class ClusterInspector : Editor {
BuildLayers();
}
protected void AddSelectorInput(INucleus nucleus) {
protected void AddSelectorInput(Nucleus nucleus) {
Selector newSelector = new(this.cluster, "New Selector");
newSelector.AddReceiver(nucleus);
this.currentNucleus = newSelector;
BuildLayers();
}
protected virtual void AddInputMemoryCell(INucleus nucleus) {
protected virtual void AddInputMemoryCell(Nucleus nucleus) {
MemoryCell newMemory = new(this.cluster, "New memory cell");
newMemory.AddReceiver(nucleus);
this.currentNucleus = newMemory;
BuildLayers();
}
protected virtual void AddCluster(INucleus nucleus) {
protected virtual void AddCluster(Nucleus nucleus) {
ClusterPickerWindow.ShowPicker(brain => OnClusterPicked(nucleus, brain), "Select Cluster");
}
private void OnClusterPicked(INucleus nucleus, ClusterPrefab prefab) {
private void OnClusterPicked(Nucleus nucleus, ClusterPrefab prefab) {
Cluster subclusterInstance = new(prefab, this.cluster);
subclusterInstance.AddReceiver(nucleus);
// This does not work somehow
@ -672,7 +672,7 @@ public class ClusterInspector : Editor {
}
// Connect to another nucleus in the same cluster
protected virtual void ConnectNucleus(ClusterPrefab cluster, INucleus nucleus) {
protected virtual void ConnectNucleus(ClusterPrefab cluster, Nucleus nucleus) {
if (cluster == null)
return;
@ -692,7 +692,7 @@ public class ClusterInspector : Editor {
// Nucleus n = this.currentNucleus.brain.nuclei[selectedIndex - perceptei.Count()];
// n.AddReceiver(this.currentNucleus);
// }
INucleus receptor = cluster.nuclei[selectedIndex];
Nucleus receptor = cluster.nuclei[selectedIndex];
receptor.AddReceiver(this.currentNucleus);
}
}
@ -734,17 +734,17 @@ public class ClusterInspector : Editor {
public class NeuroidLayer {
public int ix = 0;
public List<INucleus> neuroids = new();
public List<Nucleus> neuroids = new();
}
public class ClusterWrapper : ScriptableObject {
// expose fields that map to GraphNode
//public string title;
public Vector2 position;
INucleus node;
Nucleus node;
ClusterPrefab graph; // needed to write back and mark dirty
public ClusterWrapper Init(INucleus node, ClusterPrefab graphAsset) {
public ClusterWrapper Init(Nucleus node, ClusterPrefab graphAsset) {
this.node = node;
this.graph = graphAsset;
//this.title = " A " + node.name;

View File

@ -1,302 +0,0 @@
/*
using UnityEditor;
using UnityEngine;
using System.Linq;
using System.Collections.Generic;
public class NeuroidLayer {
public int ix = 0;
public List<Nucleus> neuroids = new();
}
public class GraphEditorWindow : EditorWindow {
private Nucleus currentNucleus;
private List<Neuroid> allNeuroids;
private Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
private List<NeuroidLayer> layers = new();
private void OnEnable() {
EditorApplication.update += EditorUpdate;
Selection.selectionChanged += OnSelectionChange;
SelectNeuron();
}
private void AddToLayer(NeuroidLayer layer, Nucleus nucleus) {
layer.neuroids.Add(nucleus);
nucleus.layerIx = layer.ix;
// Store its position
Vector2Int neuroidPosition = new(layer.ix, layer.neuroids.Count - 1);
neuroidPositions[nucleus] = neuroidPosition;
}
private void BuildLayers() {
// A temporary list to track what's been added to layers
this.layers = new();
int layerIx = 0;
Nucleus selectedNucleus = this.currentNucleus;
if (selectedNucleus == null)
return;
NeuroidLayer currentLayer = new() { ix = layerIx };
//foreach (Nucleus outputNeuroid in selectedNucleus.receivers) {
foreach (Receiver receiver in selectedNucleus.receivers) {
Nucleus outputNeuroid = receiver.nucleus;
if (outputNeuroid != null) {
AddToLayer(currentLayer, outputNeuroid);
Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
}
}
if (currentLayer.neuroids.Count > 0) {
this.layers.Add(currentLayer);
layerIx++;
currentLayer = new() { ix = layerIx };
}
AddToLayer(currentLayer, selectedNucleus);
this.layers.Add(currentLayer);
Debug.Log($"layer {layerIx} nucleus {selectedNucleus.name}");
layerIx++;
currentLayer = new() { ix = layerIx };
int six = 0;
// foreach (Synapse synapse in selectedNucleus.synapses.Values) {
// Debug.Log($"Synapse {six}");
// Nucleus input = synapse.neuroid;
//foreach ((Nucleus input, Synapse synapse) in selectedNucleus.synapses) {
//foreach ((Nucleus input, float weight) in selectedNucleus.synapses) {
foreach (Synapse synapse in selectedNucleus.synapses) {
Nucleus input = synapse.nucleus;
if (input != null) {
AddToLayer(currentLayer, input);
Debug.Log($"layer {layerIx} nucleus {input.name}");
}
six++;
}
if (currentLayer.neuroids.Count > 0) {
this.layers.Add(currentLayer);
}
}
private void BuildLayers_old(List<Neuroid> neuroids) {
if (neuroids == null)
return;
// A temporary list to track what's been added to layers
this.layers = new();
HashSet<Neuroid> neuronVisited = new();
int layerIx = 0;
// While there are unvisited neuroid
while (neuroids.Any(neuroid => !neuronVisited.Contains(neuroid))) {
// Create the next layer
NeuroidLayer currentLayer = new() { ix = layerIx };
int neuroidIx = 0;
foreach (Neuroid neuroid in neuroids) {
// Skip neurons we already processed
if (neuronVisited.Contains(neuroid))
continue;
// if (neuroid.IsStale()) {
// Debug.Log($"neuron {neuroid.name} is stale {neuroid.stale}");
// neuronVisited.Add(neuroid);
// continue;
// }
// If the output neuroid is visited
// Note: this does not yet work for multiple outputs yet (see the use of First())
// if (neuroid.receivers.Count == 0 // make sure the root neuroids are processed directly
// || (neuronVisited.Contains(neuroid.receivers.First()) && neuroid.receivers.First().layerIx == layerIx - 1)) {
if (neuroid.receivers.Count == 0 // make sure the root neuroids are processed directly
|| (neuronVisited.Contains(neuroid.receivers.First().nucleus) && neuroid.receivers.First().nucleus.layerIx == layerIx - 1)) {
// Add it to the next layer
currentLayer.neuroids.Add(neuroid);
neuroid.layerIx = layerIx;
// Register it as visited
neuronVisited.Add(neuroid);
// Store its position
Vector2Int neuroidPosition = new(layerIx, neuroidIx);
neuroidPositions[neuroid] = neuroidPosition;
neuroidIx++;
Debug.Log($"Layer {layerIx} neuron {neuroidIx} name {neuroid.name}");
}
}
if (currentLayer.neuroids.Count > 0) {
this.layers.Add(currentLayer);
layerIx++;
}
}
}
private void OnDisable() {
EditorApplication.update -= EditorUpdate;
Selection.selectionChanged -= OnSelectionChange;
}
private void OnSelectionChange() {
SelectNeuron();
Repaint();
}
private void EditorUpdate() {
if (EditorApplication.isPlaying)
Repaint();
}
private void OnGUI() {
GUILayout.Label("Graph Visualizer", EditorStyles.boldLabel);
DrawGraph();
}
private void DrawGraph() {
if (currentNucleus == null)
return;
foreach (NeuroidLayer layer in layers)
DrawLayer(layer);
}
private void DrawLayer(NeuroidLayer layer) {
int column = layer.ix * 100;
int nodeCount = layer.neuroids.Count;
float maxValue = 0;
foreach (Nucleus nucleus in layer.neuroids) {
if (nucleus is Neuroid neuroid) {
float value = neuroid.outputValue.magnitude;
if (value > maxValue)
maxValue = value;
}
}
float spacing = 400f / nodeCount;
float margin = 100 + spacing / 2;
foreach (Nucleus layerNucleus in layer.neuroids) {
if (layerNucleus is Neuroid layerNeuroid) {
Vector2Int layerNeuroidPos = this.neuroidPositions[layerNeuroid];
Vector3 parentPos = new(100 + layerNeuroidPos.x * 100, margin + layerNeuroidPos.y * spacing, 0.1f);
int i = 0;
float inputSpacing = 400f / layerNeuroid.synapses.Count;
float inputMargin = 100 + inputSpacing / 2;
// foreach (Synapse synapse in layerNeuroid.synapses.Values) {
// if (synapse.neuroid != null) {
// if (this.neuroidPositions.ContainsKey(synapse.neuroid)) {
// Vector2Int inputNeuroidPos = this.neuroidPositions[synapse.neuroid];
//foreach ((Nucleus neuroid, Synapse synapse) in layerNeuroid.synapses) {
//foreach ((Nucleus neuroid, float weight) in layerNeuroid.synapses) {
foreach (Synapse synapse in layerNeuroid.synapses) {
Nucleus neuroid = synapse.nucleus;
float weight = synapse.weight;
if (neuroid != null) {
if (this.neuroidPositions.ContainsKey(neuroid)) {
Vector2Int inputNeuroidPos = this.neuroidPositions[neuroid];
if (inputNeuroidPos.x == layerNeuroidPos.x + 1) {
Vector3 pos = new(100 + inputNeuroidPos.x * 100, inputMargin + inputNeuroidPos.y * inputSpacing, 0.0f);
//float brightness = synapse.weight / 10.0f;
float brightness = weight / 10.0f;
Handles.color = new Color(brightness, brightness, brightness);
Handles.DrawLine(parentPos, pos);
}
}
}
}
float size = 20;
if (layerNeuroid.isSleeping)
Handles.color = Color.black;
else {
float brightness = layerNeuroid.outputValue.magnitude / maxValue;
Handles.color = new Color(brightness, brightness, brightness);
}
Handles.DrawSolidDisc(parentPos, Vector3.forward, size);
Vector3 labelPos = parentPos - Vector3.down * (size + 0.2f); // below disc along up axis
GUIStyle style = new GUIStyle(EditorStyles.label) {
alignment = TextAnchor.UpperCenter,
normal = { textColor = Color.white },
fontStyle = FontStyle.Bold
};
Handles.Label(labelPos, layerNeuroid.name, style);
Rect neuronRect = new(parentPos.x - size, parentPos.y - size, size * 2, size * 2);
Event e = Event.current;
if (e != null && neuronRect.Contains(e.mousePosition)) {
HandleMouseHover(layerNeuroid, neuronRect);
// Process click
if (e.type == EventType.MouseDown && e.button == 0) {
// Consume the event so the scene doesn't also handle it
e.Use();
HandleDiscClicked(layerNeuroid);
}
}
i++;
}
}
}
private void HandleMouseHover(Neuroid neuroid, Rect rect) {
GUIContent tooltip;
// if (neuroid is SensoryNeuroid sensoryNeuroid) {
// tooltip = new(
// $"{sensoryNeuroid.name}" +
// $"\nThing {sensoryNeuroid.receptor.thingType}" +
// $"\nValue: {neuroid.outputValue}");
// }
// else {
tooltip = new(
$"{neuroid.name}" +
$"\nsynapse count {neuroid.synapses.Count}" +
$"\nValue: {neuroid.outputValue}");
// }
Vector2 mousePosition = Event.current.mousePosition;
// Display tooltip with some offset
Vector2 tooltipSize = GUI.skin.box.CalcSize(tooltip);
Rect tooltipRect = new Rect(mousePosition.x + 10, mousePosition.y + 10, tooltipSize.x, tooltipSize.y);
GUI.Box(tooltipRect, tooltip);
}
private void HandleDiscClicked(Nucleus nucleus) {
this.currentNucleus = nucleus;
BuildLayers();
}
// Update node colors based on selected GameObjects
private void SelectNeuron() {
GameObject[] selectedObjects = Selection.gameObjects;
if (selectedObjects.Length == 0)
return;
GameObject selectedObject = selectedObjects[0];
Boid boid = selectedObject.GetComponent<Boid>();
if (boid == null)
return;
// Nucleus neuroid = boid.behaviour;
// this.currentNucleus = neuroid;
// if (neuroid == null)
// this.allNeuroids = new();
// else
// this.allNeuroids = neuroid.brain.neuroids;
// Debug.Log($"Neuroncount = {this.allNeuroids.Count}");
// BuildLayers();
// Debug.Log($"Layercount = {this.layers.Count}");
}
[MenuItem("Window/Neuroid Visualizer")]
public static void ShowWindow() {
GetWindow<GraphEditorWindow>("Neuroid Visualizer");
}
}
*/

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 26e68838038ea5243ae57bc81f4db8a8

View File

@ -1,3 +1,4 @@
/*
using System.Collections.Generic;
using Unity.Mathematics;
@ -11,7 +12,7 @@ public interface INucleus {
// Senders
public List<Synapse> synapses { get; }
public Synapse AddSynapse(INucleus sender, float weight = 1.0f);
public Synapse AddSynapse(Nucleus sender, float weight = 1.0f);
public NucleusArray array { get; set; }
@ -21,8 +22,8 @@ public interface INucleus {
// public void UpdateState();
// public void UpdateState(float3 inputValue);
public void UpdateStateIsolated();
public void UpdateStateIsolated(float3 inputValue);
// public void UpdateStateIsolated();
// public void UpdateStateIsolated(float3 inputValue);
#endregion dynamic state
@ -31,10 +32,10 @@ public interface INucleus {
public string name { get; set; }
// Receivers
public List<INucleus> receivers { get; set; }
// public List<Nucleus> receivers { get; set; }
public void AddReceiver(INucleus receiver, float weight = 1);
public void RemoveReceiver(INucleus receiverNucleus);
// public void AddReceiver(Nucleus receiver, float weight = 1);
// public void RemoveReceiver(Nucleus receiverNucleus);
#endregion static
@ -48,10 +49,11 @@ public interface INucleus {
#endregion dynamic
public INucleus ShallowCloneTo(Cluster parent);
public INucleus Clone();
// public INucleus ShallowCloneTo(Cluster parent);
// public INucleus Clone();
}
// public interface IReceptor {
// }
*/

View File

@ -13,7 +13,7 @@ public class MemoryCell : Neuron {
// this.parent?.nuclei.Add(this);
// }
public override INucleus ShallowCloneTo(Cluster newParent) {
public override Nucleus ShallowCloneTo(Cluster newParent) {
MemoryCell clone = new(newParent, this.name) {
array = this.array,
curve = this.curve,

View File

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

View File

@ -181,7 +181,7 @@ public class Neuron : Nucleus {
#endregion Runtime state
// this clone the nucleus without the synapses and receivers
public override INucleus ShallowCloneTo(Cluster newParent) {
public override Nucleus ShallowCloneTo(Cluster newParent) {
Neuron clone = new(newParent, this.name) {
array = null,
curve = this.curve,
@ -192,9 +192,9 @@ public class Neuron : Nucleus {
return clone;
}
public override INucleus Clone() {
//Neuron clone = new(this.cluster, this.name) {
Neuron clone = new(this.parent, this.name) {
public override Nucleus Clone() {
Neuron clone = new(this.cluster, this.name) {
//Neuron clone = new(this.parent, this.name) {
array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
@ -208,7 +208,7 @@ public class Neuron : Nucleus {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (INucleus receiver in this.receivers) {
foreach (Nucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
return clone;
@ -224,7 +224,7 @@ public class Neuron : Nucleus {
// receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
// }
public static void Delete(INucleus nucleus) {
public static void Delete(Nucleus nucleus) {
foreach (Synapse synapse in nucleus.synapses) {
if (synapse.nucleus is Neuron synapse_nucleus) {
if (synapse_nucleus.receivers.Count > 1) {
@ -237,7 +237,7 @@ public class Neuron : Nucleus {
}
}
}
foreach (INucleus receiver in nucleus.receivers) {
foreach (Nucleus receiver in nucleus.receivers) {
if (receiver != null && receiver.synapses != null)
receiver.synapses.RemoveAll(s => s.nucleus == nucleus);
}

View File

@ -4,7 +4,8 @@ using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
public abstract class Nucleus : INucleus {
[Serializable]
public abstract class Nucleus {
[SerializeField]
protected string _name;
public virtual string name {
@ -31,10 +32,8 @@ public abstract class Nucleus : INucleus {
[NonSerialized]
public int stale = 1000;
// Cannot clone an abstract nucleus...
public virtual INucleus ShallowCloneTo(Cluster parent) { return null; }
// Cannot clone an abstract nucleus...
public virtual INucleus Clone() { return null; }
public abstract Nucleus ShallowCloneTo(Cluster parent);
public abstract Nucleus Clone();
#region Synapses
@ -42,13 +41,13 @@ public abstract class Nucleus : INucleus {
private List<Synapse> _synapses = new();
public List<Synapse> synapses => _synapses;
public Synapse AddSynapse(INucleus sendingNucleus, float weight = 1.0f) {
public Synapse AddSynapse(Nucleus sendingNucleus, float weight = 1.0f) {
Synapse synapse = new(sendingNucleus, weight);
this.synapses.Add(synapse);
return synapse;
}
public Synapse GetSynapse(INucleus sender) {
public Synapse GetSynapse(Nucleus sender) {
foreach (Synapse synapse in this.synapses)
if (synapse.nucleus == sender)
return synapse;
@ -60,18 +59,18 @@ public abstract class Nucleus : INucleus {
#region Receivers
[SerializeReference]
private List<INucleus> _receivers = new();
public List<INucleus> receivers {
private List<Nucleus> _receivers = new();
public List<Nucleus> receivers {
get { return _receivers; }
set { _receivers = value; }
}
public virtual void AddReceiver(INucleus receivingNucleus, float weight = 1) {
public virtual void AddReceiver(Nucleus receivingNucleus, float weight = 1) {
this._receivers.Add(receivingNucleus);
receivingNucleus.AddSynapse(this, weight);
}
public void RemoveReceiver(INucleus receiverNucleus) {
public void RemoveReceiver(Nucleus receiverNucleus) {
this._receivers.RemoveAll(receiver => receiver == receiverNucleus);
receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
}

View File

@ -5,26 +5,26 @@ using UnityEngine;
[System.Serializable]
public class NucleusArray {
[SerializeReference]
private INucleus[] _nuclei;
public INucleus[] nuclei {
private Nucleus[] _nuclei;
public Nucleus[] nuclei {
get {
return _nuclei;
}
}
public string name;
public NucleusArray(INucleus nucleus) {
public NucleusArray(Nucleus nucleus) {
this.name = nucleus.name;
this._nuclei = new INucleus[1];
this._nuclei = new Nucleus[1];
this._nuclei[0] = nucleus;
}
public NucleusArray(ClusterPrefab cluster) {
this.name = cluster.name;
this._nuclei = new INucleus[0];
this._nuclei = new Nucleus[0];
}
public NucleusArray(int size, string name) {
this.name = name;
this._nuclei = new INucleus[size];
this._nuclei = new Nucleus[size];
}
@ -34,12 +34,12 @@ public class NucleusArray {
return;
}
int newLength = this._nuclei.Length + 1;
INucleus[] newArray = new INucleus[newLength];
Nucleus[] newArray = new Nucleus[newLength];
for (int i = 0; i < this._nuclei.Length; i++)
newArray[i] = this._nuclei[i];
if (this._nuclei[0] is INucleus nucleus)
newArray[newLength - 1] = (INucleus)nucleus.Clone();
if (this._nuclei[0] is Nucleus nucleus)
newArray[newLength - 1] = nucleus.Clone();
this._nuclei = newArray;
}
@ -50,25 +50,25 @@ public class NucleusArray {
Debug.LogWarning("Perceptoid array cannot be empty");
return;
}
INucleus[] newPerceptei = new INucleus[newLength];
Nucleus[] newPerceptei = new Nucleus[newLength];
for (int i = 0; i < newLength; i++)
newPerceptei[i] = this._nuclei[i];
// Delete the last perception
if (this._nuclei[newLength] is INucleus nucleus)
if (this._nuclei[newLength] is Nucleus nucleus)
Neuron.Delete(nucleus); //this._nuclei[newLength]);
this._nuclei = newPerceptei;
}
public Dictionary<int, INucleus> thingReceivers = new();
public Dictionary<int, Nucleus> thingReceivers = new();
public virtual void ProcessStimulus(int thingId, Vector3 inputValue, string thingName = null) {
CleanupReceivers();
if (!thingReceivers.TryGetValue(thingId, out INucleus selectedReceiver)) {
if (!thingReceivers.TryGetValue(thingId, out Nucleus selectedReceiver)) {
Debug.Log($"No receiver found for {thingId}");
foreach (INucleus receptor in this.nuclei) {
if (receptor is not INucleus receiver)
foreach (Nucleus receptor in this.nuclei) {
if (receptor is not Nucleus receiver)
continue;
if (thingReceivers.ContainsValue(receiver) == false) {
@ -97,7 +97,7 @@ public class NucleusArray {
private void CleanupReceivers() {
// Remove a thing-receiver connection when the nucleus is inactive
List<int> receiversToRemove = new();
foreach (KeyValuePair<int, INucleus> item in thingReceivers) {
foreach (KeyValuePair<int, Nucleus> item in thingReceivers) {
if (item.Value.isSleeping) {
Nucleus n = item.Value as Nucleus;
Debug.Log($"{item.Value.name} is sleeping, stale = {n.stale}");
@ -105,7 +105,7 @@ public class NucleusArray {
}
}
foreach (int thingId in receiversToRemove) {
INucleus selectedReceiver = thingReceivers[thingId];
Nucleus selectedReceiver = thingReceivers[thingId];
thingReceivers.Remove(thingId);
Debug.Log($"Cleanup receiver for {thingId}");

View File

@ -7,7 +7,7 @@ public class Selector : Neuron {
public Selector(Cluster parent, string name) : base(parent, name) { }
public Selector(ClusterPrefab parent, string name) : base(parent, name) {}
public override INucleus ShallowCloneTo(Cluster newParent) {
public override Nucleus ShallowCloneTo(Cluster newParent) {
Selector clone = new(newParent, this.name) {
array = this.array,
curve = this.curve,

View File

@ -4,11 +4,11 @@ using UnityEngine;
[Serializable]
public class Synapse {
[SerializeReference]
public INucleus nucleus;
public Nucleus nucleus;
public float weight;
public Synapse(INucleus nucleus, float weight = 1.0f) {
public Synapse(Nucleus nucleus, float weight = 1.0f) {
this.nucleus = nucleus;
this.weight = weight;
}