This commit is contained in:
Pascal Serrarens 2026-03-05 16:04:28 +01:00
parent 8a9700981b
commit ccb7a41577
12 changed files with 2 additions and 522 deletions

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
@ -48,7 +47,6 @@ public class Cluster : Nucleus {
Nucleus[] prefabNuclei = this.prefab.nuclei.ToArray();
// first clone the nuclei without their connections
foreach (Nucleus nucleus in this.prefab.nuclei) {
// Debug.Log($"prefab clone {nucleus.name}");
nucleus.ShallowCloneTo(this);
}
Nucleus[] clonedNuclei = this.clusterNuclei.ToArray();
@ -477,9 +475,6 @@ public class Cluster : Nucleus {
Debug.Log($" {nucleus.name}[{nucleus.GetHashCode()}] = {neuron.outputValue}");
}
// this.outputValue = this.defaultOutput.outputValue;
// this.stale = 0;
// continue in parent
this.parent?.UpdateFromNucleus(this);
@ -494,7 +489,6 @@ public class Cluster : Nucleus {
if (synapse.nucleus is Neuron neuron) {
if (lengthsq(neuron.outputValue) > 0) {
sum += synapse.weight * neuron.outputValue;
//this.stale = 0;
}
}
}
@ -502,9 +496,6 @@ public class Cluster : Nucleus {
foreach (Nucleus nucleus in this.sortedNuclei)
nucleus.UpdateStateIsolated();
// this.outputValue = this.defaultOutput.outputValue;
// this.stale = 0;
UpdateNuclei();
}

View File

@ -64,7 +64,6 @@ public class ClusterPrefab : ScriptableObject {
HashSet<Nucleus> visitedNuclei = new();
foreach (Nucleus output in this.outputs)
MarkNuclei(visitedNuclei, output);
//MarkNuclei(visitedNuclei, this.output);
//Debug.Log($"Garbage collection found {visitedNuclei.Count} Nuclei");
this.nuclei.RemoveAll(nucleus => visitedNuclei.Contains(nucleus) == false);
}

View File

@ -26,12 +26,7 @@ public class ClusterReceptor : Cluster, IReceptor {
public override Nucleus ShallowCloneTo(Cluster parent) {
ClusterReceptor clone = new(this.prefab, parent, this.name) {
clusterPrefab = this.clusterPrefab,
//array = this.array
};
//CloneFields(clone);
// This cloned the prefab with the clusternuclei,
// but did not clone the receivers outside the cluster
//RestoreExternalReceivers(clone, this.clusterPrefab, parent);
return clone;
}
@ -41,16 +36,11 @@ public class ClusterReceptor : Cluster, IReceptor {
array = this._array
};
//CloneFields(clone);
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
// foreach (Nucleus receiver in this.receivers) {
// clone.AddReceiver(receiver);
// }
this._outputs = null; // Make sure the output are regenerated
foreach (Neuron output in this.outputs) {
int ix = GetNucleusIndex(this.clusterNuclei, output);
@ -89,19 +79,14 @@ public class ClusterReceptor : Cluster, IReceptor {
[SerializeReference]
private NucleusArray _array;
public NucleusArray array {
//get { return _array; }
set { _array = value; }
}
//[SerializeReference]
//private Nucleus[] _nucleusArray;
public Nucleus[] nucleiArray {
get { return _array.nuclei; }
set { _array.nuclei = value; }
}
//public ClusterReceptor[] nucleiArray;
public void AddReceptorElement(ClusterPrefab prefab) {
IReceptorHelpers.AddReceptorElement(this, prefab);
}
@ -118,32 +103,14 @@ public class ClusterReceptor : Cluster, IReceptor {
// Clusters don't do anything,
// The nuclei in them do the work
// and should be called directly, not from the cluster
// float3 sum = this.bias;
// foreach (Nucleus nucleus in this.sortedNuclei)
// nucleus.UpdateStateIsolated();
// this.outputValue = this.defaultOutput.outputValue;
// this.stale = 0;
// UpdateNuclei();
}
public override void UpdateNuclei() {
// this.stale++;
// if (this.stale > staleValueForSleep && lengthsq(this.bias) > 0) {
// this.bias = new float3(0, 0, 0);
// this.parent.UpdateFromNucleus(this);
// }
foreach (Nucleus nucleus in this.clusterNuclei)
nucleus.UpdateNuclei();
}
public override void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null) {
//this._array ??= new NucleusArray(this.parent);
//this._array.ProcessStimulus(thingId, inputValue, thingName);
Debug.LogError("Process Stimulus was called on clusterreceptor without a neuron specified");
}
@ -152,9 +119,6 @@ public class ClusterReceptor : Cluster, IReceptor {
public virtual void ProcessStimulus(Neuron input, Vector3 inputValue, int thingId = 0, string thingName = null) {
CleanupReceivers();
// Debug.Log($"{input.name} is {inputValue}, 1/{inputValue} = {1 / inputValue.x}, {1/inputValue.y}, {1/inputValue.z}");
//inputValue = input.Activator(inputValue);
if (!thingReceivers.TryGetValue(thingId, out ClusterReceptor selectedReceiver))
selectedReceiver = FindReceiver2(thingId, inputValue, input);
if (selectedReceiver == null)
@ -174,52 +138,8 @@ public class ClusterReceptor : Cluster, IReceptor {
if (selectedReceiver.clusterNuclei[inputIx] is Neuron selectedNeuron)
selectedNeuron.ProcessStimulusDirect(inputValue);
// Debug.Log($"cluster output = {selectedReceiver.defaultOutput.outputValue}");
}
// private ClusterReceptor FindReceiver(int thingId, float3 inputValue) {
// // No existing nucleus for this thing
// float inputMagnitude = length(inputValue);
// ClusterReceptor selectedReceiver = null;
// float selectedMagnitude = 0;
// foreach (ClusterReceptor receiver in nucleiArray.Cast<ClusterReceptor>()) {
// if (thingReceivers.ContainsValue(receiver) == false) {
// // We found an unusued receiver
// thingReceivers.Add(thingId, receiver);
// return receiver;
// }
// else if (receiver.isSleeping) {
// // A sleeping receiver is not active and can therefore always be used
// thingReceivers.Add(thingId, receiver);
// return receiver;
// }
// else if (selectedReceiver == null) {
// // If we haven't found a receiver yet, just start by taking the first
// selectedReceiver = receiver;
// selectedMagnitude = length(selectedReceiver.outputValue);
// }
// // Look for the receiver with the lowest magnitude
// else {
// float magnitude = length(receiver.outputValue);
// if (magnitude < inputMagnitude && length(receiver.outputValue) < selectedMagnitude) {
// selectedReceiver = receiver;
// selectedMagnitude = length(selectedReceiver.outputValue);
// }
// }
// }
// if (selectedReceiver != null) {
// // Replace the receiver
// // Find the thingId current associated with the receiver
// int keyToRemove = thingReceivers.FirstOrDefault(r => r.Value.Equals(selectedReceiver)).Key;
// if (keyToRemove != 0 || thingReceivers.ContainsKey(keyToRemove))
// thingReceivers.Remove(keyToRemove);
// // And add the new association
// thingReceivers.Add(thingId, selectedReceiver);
// }
// return selectedReceiver;
// }
private ClusterReceptor FindReceiver2(int thingId, float3 inputValue, Neuron input) {
// No existing nucleus for this thing
ClusterReceptor selectedReceiver = null;

View File

@ -3,9 +3,6 @@ using UnityEngine;
public interface IReceptor {
public string GetName();
// public NucleusArray array {
// get; set;
// }
public Nucleus[] nucleiArray { get; set; }
public void AddReceptorElement(ClusterPrefab prefab);
@ -54,7 +51,7 @@ public static class IReceptorHelpers {
newArray[i] = receptor.nucleiArray[i];
// Delete the last perception
if (receptor.nucleiArray[newLength] is Nucleus nucleus)
Neuron.Delete(nucleus); //this._nuclei[newLength]);
Neuron.Delete(nucleus);
foreach (Nucleus element in receptor.nucleiArray) {
if (element is IReceptor receptorElement) {

View File

@ -1,7 +1,5 @@
using System;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[Serializable]
public class MemoryCell : Neuron {

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using Unity.Mathematics;
@ -303,18 +302,8 @@ public class Neuron : Nucleus {
}
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) {
@ -341,9 +330,6 @@ public class Neuron : Nucleus {
}
else
ProcessStimulusDirect(inputValue, thingId, thingName);
// this.stale = 0;
// this.bias = inputValue;
// this.parent.UpdateFromNucleus(this);
}
public void ProcessStimulusDirect(Vector3 inputValue, int thingId = 0, string thingName = null) {

View File

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[Serializable]
public abstract class Nucleus {
@ -13,22 +11,6 @@ public abstract class Nucleus {
[SerializeReference]
public Cluster parent;
// protected float3 _outputValue;
// public virtual float3 outputValue {
// get { return _outputValue; }
// set {
// _outputValue = value;
// if (this.isFiring)
// WhenFiring?.Invoke();
// }
// }
// public bool isFiring => length(_outputValue) > 0.5f;
// public Action WhenFiring;
// public virtual bool isSleeping => lengthsq(this.outputValue) == 0;
// [NonSerialized]
// public int stale = 1000;
// public readonly int staleValueForSleep = 20;
public bool trace = false;
public abstract Nucleus ShallowCloneTo(Cluster parent);
@ -38,11 +20,8 @@ public abstract class Nucleus {
None,
Neuron,
MemoryCell,
Selector,
Cluster,
Pulsar,
Receptor,
// ReceptorArray,
ClusterReceptor,
}
@ -81,7 +60,6 @@ public abstract class Nucleus {
}
public virtual void SetBias(Vector3 inputValue) {
//this.stale = 0;
this.bias = inputValue;
this.parent.UpdateFromNucleus(this);
}

View File

@ -1,105 +0,0 @@
/*
using UnityEngine;
[System.Serializable]
public class Perceptoid : Neuroid {
// A neuroid which has no neurons as input
// But receives value from a receptor
public NanoBrain brain;
public Receptor receptor;
public string baseName;
public int thingId;
//[SerializeField]
// Needs serialization!!!!
[SerializeReference]
public PercepteiArray array;
#region Serialization
[SerializeField]
public int thingType;
public override void Rebuild(NanoBrain brain) {
base.Rebuild(brain);
this.receptor = Receptor.GetReceptor(brain, thingType);
this.receptor.perceptei.Add(this);
if (string.IsNullOrEmpty(this.baseName))
this.baseName = this.name;
}
public override void Deserialize(Nucleus nucleus) {
base.Deserialize(nucleus);
if (nucleus is Perceptoid perceptoid)
this.receptor.thingType = perceptoid.thingType;
// Point all receivers to this perceptoid instead of the default nucleus
foreach (INucleus receiver in nucleus.receivers) {
foreach (Synapse synapse in receiver.synapses) {
if (synapse.nucleus == nucleus)
synapse.nucleus = this;
}
}
// Point all synapses to this perceptoid instead of the default nucleus
// foreach (Synapse synapse in nucleus.synapses) {
// foreach (INucleus r in synapse.nucleus.receivers) {
// if (r == nucleus)
// this.receiver = this;
// }
// }
// Copying disabled for now
// // Copy all the synapses
// this.synapses = nucleus.synapses;
// // Copy all receivers
// this.receivers = nucleus.receivers;
}
#endregion Serialization
public Perceptoid(NanoBrain brain, int thingType, string name = "sensor") : base(name) {
this.brain = brain;
this.cluster = brain.cluster;
if (this.cluster != null) {
brain.perceptei.Add(this);
}
else
Debug.LogError("No neuroid network");
this.nucleusType = nameof(Perceptoid);
this.name = name;
this.baseName = name;
this.thingType = thingType;
this.receptor = Receptor.GetReceptor(brain, thingType);
this.receptor.perceptei.Add(this);
this.array = new PercepteiArray(this);
}
public Perceptoid(PercepteiArray array) : base(array.name) {
this.array = array;
Perceptoid source = array.perceptei[0];
this.brain = source.brain;
this.cluster = source.cluster;
if (this.brain != null) {
this.brain.perceptei.Add(this);
}
else
Debug.LogError("No neuroid network");
this.nucleusType = nameof(Perceptoid);
this.name = source.baseName;
this.baseName = source.baseName;
this.thingType = source.thingType;
this.receptor = Receptor.GetReceptor(this.brain, this.thingType);
this.receptor.perceptei.Add(this);
}
public override void UpdateState() {
Vector3 result = this.receptor.localPosition;
UpdateResult(result);
}
}
*/

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 702f634001a21a9d7ae1057c8ce356e9

View File

@ -48,36 +48,19 @@ public class Receptor : Neuron, IReceptor {
}
public void AddReceptorElement(ClusterPrefab prefab) {
//this.nucleiArray = IReceptorHelpers.AddReceptorElement(this.nucleiArray, prefab);
IReceptorHelpers.AddReceptorElement(this, prefab);
}
public void RemoveReceptorElement() {
// this.nucleiArray = IReceptorHelpers.RemoveReceptorElement(this.nucleiArray);
IReceptorHelpers.RemoveReceptorElement(this);
}
// public override void AddReceiver(Nucleus receiverToAdd, float weight = 1) {
// foreach (Nucleus element in receptorToAdd.nucleiArray) {
// if (element is Neuron neuron) {
// neuron._receivers.Add(receiverToAdd);
// receiverToAdd.AddSynapse(element, weight);
// }
// }
// }
public virtual void AddArrayReceiver(Nucleus receiverToAdd, float weight = 1) {
IReceptorHelpers.AddArrayReceiver(this, receiverToAdd, weight);
// foreach (Nucleus element in this._array.nuclei) {
// if (element is Neuron neuron) {
// neuron.AddReceiver(receiverToAdd, weight);
// }
// }
}
public override void UpdateStateIsolated() {
this.outputValue = this.bias;
//Debug.Log($"Receptor {this.name} outputvalue = {this.outputValue}");
}
public override void UpdateNuclei() {

View File

@ -1,263 +0,0 @@
/*
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[Serializable]
public class ReceptorInstance : Nucleus {
public ReceptorInstance(Cluster parent, string name) {
this.parent = parent;
this.name = name;
// We explicitly do not add this to the parent, as it is serialized in the ReceptorArray
}
public ReceptorInstance(ClusterPrefab prefab, string name) {
this.clusterPrefab = prefab;
this.name = name;
// We explicitly do not add this to the prefab, as it is serialized in the ReceptorArray
}
public override Nucleus ShallowCloneTo(Cluster parent) {
ReceptorInstance clone = new(parent, name + " +1") {
receptor = this.receptor
};
return clone;
}
public override Nucleus Clone(ClusterPrefab prefab) {
ReceptorInstance clone = new(prefab, name) {
receptor = this.receptor
};
return clone;
}
[SerializeReference]
public ReceptorArray receptor;
public override void UpdateStateIsolated() {
}
}
[Serializable]
public class ReceptorArray : Nucleus {
public ReceptorArray(Cluster parent, string name) {
this.parent = parent;
this.name = name;
this._instances = new ReceptorInstance[1];
this._instances[0] = new ReceptorInstance(parent, this.name + "[0]") {
receptor = this
};
this.parent?.clusterNuclei.Add(this);
}
public ReceptorArray(ClusterPrefab prefab, string name) {
this.clusterPrefab = prefab;
this.name = name;
this._instances = new ReceptorInstance[1];
this._instances[0] = new ReceptorInstance(prefab, this.name + "[0]") {
receptor = this
};
if (this.clusterPrefab != null)
this.clusterPrefab.nuclei.Add(this);
else
Debug.LogError("No prefab when adding receptor to prefab");
}
public override Nucleus ShallowCloneTo(Cluster parent) {
ReceptorArray clone = new(parent, name) {
_instances = new ReceptorInstance[this.instances.Length]
};
for (int ix = 0; ix < this.instances.Length; ix++) {
clone._instances[ix] = new ReceptorInstance(parent, $"{this.name} [{ix}]") {
receptor = clone
};
}
return clone;
}
public override Nucleus Clone(ClusterPrefab prefab) {
ReceptorArray clone = new(prefab, this.name) {
_instances = new ReceptorInstance[this.instances.Length]
};
for (int ix = 0; ix < this.instances.Length; ix++) {
clone._instances[ix] = new ReceptorInstance(prefab, this.name) {
receptor = this
};
}
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
// foreach (Nucleus receiver in this.receivers) {
// clone.AddReceiver(receiver);
// }
return clone;
}
[SerializeReference]
private ReceptorInstance[] _instances = new ReceptorInstance[0];
public ReceptorInstance[] instances {
get {
return _instances;
}
}
public void AddReceptor(ClusterPrefab prefab) {
if (this._instances.Length == 0) {
Debug.LogError("Empty receptor array, cannot add");
return;
}
int newLength = this._instances.Length + 1;
ReceptorInstance[] newArray = new ReceptorInstance[newLength];
for (int i = 0; i < this._instances.Length; i++)
newArray[i] = this._instances[i];
ReceptorInstance newReceptor = (ReceptorInstance)this._instances[0].Clone(prefab);
newReceptor.name = $"{this.name} [{this._instances.Length}]";
newArray[newLength - 1] = newReceptor;
this._instances = newArray;
}
public void RemoveReceptor() {
int newLength = this._instances.Length - 1;
if (newLength == 0) {
Debug.LogWarning("Receptor array cannot be empty");
return;
}
ReceptorInstance[] newPerceptei = new ReceptorInstance[newLength];
for (int i = 0; i < newLength; i++)
newPerceptei[i] = this._instances[i];
// Delete the last perception
if (this._instances[newLength] is Nucleus nucleus)
Neuron.Delete(nucleus); //this._nuclei[newLength]);
this._instances = newPerceptei;
}
private Dictionary<int, Nucleus> thingReceivers = new();
// public override void ProcessStimulus(int thingId, Vector3 inputValue, string thingName = null) {
// ProcessStimulus(inputValue, thingId, thingName);
// }
public override void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null) {
CleanupReceivers();
if (!thingReceivers.TryGetValue(thingId, out Nucleus selectedReceiver)) {
//Debug.Log($" no receiver found for {thingId}");
// No existing nucleus for this thing
selectedReceiver = SelectReceptor(thingId, inputValue);
}
if (selectedReceiver == null)
return;
if (thingName != null) {
string baseName = selectedReceiver.name;
int colonPos = selectedReceiver.name.IndexOf(":");
if (colonPos > 0)
baseName = selectedReceiver.name[..colonPos];
selectedReceiver.name = baseName + ": " + thingName;
}
//if (selectedReceiver is Neuron selectedNucleus) {
selectedReceiver.stale = 0;
selectedReceiver.outputValue = inputValue;
this.parent.UpdateFromNucleus(this);
//selectedNucleus.ProcessStimulus(inputValue);
//}
}
private void CleanupReceivers() {
// Remove a thing-receiver connection when the nucleus is inactive
List<int> receiversToRemove = new();
thingReceivers ??= new();
foreach (KeyValuePair<int, Nucleus> item in thingReceivers) {
if (item.Value.isSleeping)
receiversToRemove.Add(item.Key);
}
foreach (int thingId in receiversToRemove) {
Nucleus selectedReceiver = thingReceivers[thingId];
// Debug.Log($" removed receiver for {thingId}");
thingReceivers.Remove(thingId);
int colonPos = selectedReceiver.name.IndexOf(":");
if (colonPos > 0)
selectedReceiver.name = selectedReceiver.name[..colonPos];
}
}
private Nucleus SelectReceptor(int thingId, float3 inputValue) {
// No existing nucleus for this thing
float inputMagnitude = length(inputValue);
Nucleus selectedReceiver = null;
float selectedMagnitude = 0;
this._instances ??= new ReceptorInstance[0];
foreach (Nucleus receiver in this._instances) {
if (thingReceivers.ContainsValue(receiver) == false) {
// We found an unusued receiver
// Debug.Log($"{thingId} -> [{receiver.name}]");
thingReceivers.Add(thingId, receiver);
return receiver;
}
else if (receiver.isSleeping) {
// A sleeping receiver is not active and can therefore always be used
thingReceivers.Add(thingId, receiver);
Debug.Log($"{thingId} -> [{selectedReceiver.name}]");
return receiver;
}
else if (selectedReceiver == null) {
// If we haven't found a receiver yet, just start by taking the first
selectedReceiver = receiver;
selectedMagnitude = length(selectedReceiver.outputValue);
}
// Look for the receiver with the lowest magnitude
else {
float magnitude = length(receiver.outputValue);
if (magnitude < inputMagnitude && length(receiver.outputValue) < selectedMagnitude) {
selectedReceiver = receiver;
selectedMagnitude = length(selectedReceiver.outputValue);
}
}
}
if (selectedReceiver != null) {
// Replace the receiver
// Find the thingId current associated with the receiver
int keyToRemove = thingReceivers.FirstOrDefault(r => r.Value.Equals(selectedReceiver)).Key;
if (keyToRemove != 0 || thingReceivers.ContainsKey(keyToRemove))
thingReceivers.Remove(keyToRemove);
// And add the new association
thingReceivers.Add(thingId, selectedReceiver);
}
return selectedReceiver;
}
public override void UpdateStateIsolated() {
float3 sum = this.bias;
// Receptors do not have inputs, so we ignore the synapses
foreach (Nucleus nucleus in this._instances)
sum += nucleus.outputValue;
this.outputValue = sum / _instances.Length;
this.stale = 0;
UpdateNuclei();
}
public override void UpdateNuclei() {
foreach (Nucleus nucleus in this.instances) {
nucleus.stale++;
if (nucleus.stale > staleValueForSleep && lengthsq(nucleus.outputValue) > 0) {
nucleus.outputValue = Vector3.zero;
//this.UpdateStateIsolated();
this.parent.UpdateFromNucleus(this);
}
}
}
}
*/

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 9e915c8563642f23891b20522b3589cf