receptorarray is working

This commit is contained in:
Pascal Serrarens 2026-02-05 16:38:42 +01:00
parent b73b28146c
commit 0258ef1197
8 changed files with 209 additions and 49 deletions

View File

@ -41,16 +41,6 @@ public class Cluster : INucleus {
this.sortedNuclei = TopologicalSort(this.nuclei);
}
// public Cluster(ClusterPrefab parent, ClusterPrefab realPrefab) {
// this.prefab = realPrefab;
// this.name = realPrefab.name;
// this.cluster = parent;
// if (this.cluster != null)
// this.cluster.nuclei.Add(this);
// ClonePrefab();
// }
private void ClonePrefab() {
IReceptor[] nuclei = this.prefab.nuclei.ToArray();
// first clone the nuclei without their connections
@ -157,7 +147,8 @@ public class Cluster : INucleus {
}
public virtual IReceptor Clone() {
Neuron clone = new(this.cluster, this.name) {
//Neuron clone = new(this.cluster, this.name) {
Neuron clone = new(this.parent, this.name) {
array = this.array,
};
@ -232,6 +223,18 @@ public class Cluster : INucleus {
set { _array = value; }
}
public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) {
foreach (IReceptor receptor in this.nuclei) {
if (receptor is Nucleus nucleus)
if (nucleus.name == nucleusName) {
foundNucleus = nucleus;
return true;
}
}
foundNucleus = null;
return false;
}
#region Synapses
[SerializeField]
@ -321,16 +324,23 @@ public class Cluster : INucleus {
//Applying the weight factors
foreach (Synapse synapse in this.synapses) {
sum += synapse.weight * synapse.nucleus.outputValue;
if (lengthsq(synapse.nucleus.outputValue) > 0) {
sum += synapse.weight * synapse.nucleus.outputValue;
this.stale = 0;
}
}
//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])
nucleus.UpdateStateIsolated();
if (receptor is INucleus nucleus && nucleus != this.inputs[0]) {
//if (nucleus.isSleeping == false)
nucleus.UpdateStateIsolated();
}
}
this.outputValue = this.output.outputValue;
UpdateNuclei();
}
// public virtual void UpdateResult(Vector3 result) {
@ -347,7 +357,7 @@ public class Cluster : INucleus {
public void UpdateNuclei() {
this.stale++;
if (this.stale > 2)
if (this.stale > 5)
_outputValue = Vector3.zero;
//foreach (IReceptor nucleus in this.prefab.nuclei)

View File

@ -428,17 +428,17 @@ public class ClusterInspector : Editor {
private void HandleMouseHover(IReceptor nucleus, Rect rect) {
GUIContent tooltip;
if (nucleus is INucleus n) {
tooltip = new(
$"{nucleus.name}" +
$"\nsynapse count {n.synapses.Count}" +
$"\nValue: {length(nucleus.outputValue)}");
}
else {
// if (nucleus is INucleus n) {
// tooltip = new(
// $"{nucleus.name}" +
// //$"\nsynapse count {n.synapses.Count}" +
// $"\nValue: {length(nucleus.outputValue)}");
// }
// else {
tooltip = new(
$"{nucleus.name}" +
$"\nValue: {length(nucleus.outputValue)}");
}
// }
Vector2 mousePosition = Event.current.mousePosition;
@ -616,6 +616,7 @@ public class ClusterInspector : Editor {
}
protected virtual void AddInputNeuron(INucleus nucleus) {
//Neuron newNeuroid = new(this.cluster, "New neuron");
Neuron newNeuroid = new(this.cluster, "New neuron");
newNeuroid.AddReceiver(nucleus);
this.currentNucleus = newNeuroid;

View File

@ -193,7 +193,8 @@ public class Neuron : Nucleus, INucleus {
}
public override IReceptor Clone() {
Neuron clone = new(this.cluster, this.name) {
//Neuron clone = new(this.cluster, this.name) {
Neuron clone = new(this.parent, this.name) {
array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
@ -299,8 +300,10 @@ public class Neuron : Nucleus, INucleus {
// public virtual void UpdateStateIsolated() {
// UpdateStateIsolated(new float3(0, 0, 0));
// }
public override void UpdateStateIsolated(float3 bias) {
float3 sum = bias;
public float3 bias = float3(0, 0, 0);
public override void UpdateStateIsolated(float3 bias_unused) {
float3 sum = this.bias;
int n = 0;
//Applying the weight factgors
@ -308,8 +311,10 @@ public class Neuron : Nucleus, INucleus {
sum += synapse.weight * synapse.nucleus.outputValue;
// Perhaps synapses should be removed when the output value goes to 0....
if (lengthsq(synapse.nucleus.outputValue) != 0)
if (lengthsq(synapse.nucleus.outputValue) != 0) {
n++;
this.stale = 0;
}
}
if (this.average && n > 0)
sum /= n;
@ -337,7 +342,17 @@ public class Neuron : Nucleus, INucleus {
result = normalize(sum) * activatedValue;
break;
}
this.outputValue = result;
if (this.stale > 5)
this.outputValue = new float3(0,0,0);
else
this.outputValue = result;
}
public virtual void ProcessStimulus(Vector3 inputValue, string thingName = null) {
//this.outputValue = inputValue;
this.stale = 0;
Debug.Log($"{this.name} processed stimulus");
this.bias = inputValue;
}
// public virtual void UpdateResult(Vector3 result) {

View File

@ -20,7 +20,8 @@ public abstract class Nucleus : IReceptor {
public virtual float3 outputValue {
get { return _outputValue; }
set {
this.stale = 0;
//Debug.Log($"{this.name}: stale is reset, was: {this.stale}");
//this.stale = 0;
// this._isSleeping = false;
_outputValue = value;
}
@ -28,7 +29,7 @@ public abstract class Nucleus : IReceptor {
public bool isSleeping => lengthsq(this.outputValue) == 0;
[NonSerialized]
private int stale = 1000;
public int stale = 1000;
// Cannot clone an abstract nucleus...
public virtual IReceptor ShallowCloneTo(Cluster parent) { return null; }
@ -96,8 +97,14 @@ public abstract class Nucleus : IReceptor {
public void UpdateNuclei() {
this.stale++;
if (this.stale > 2)
if (this.stale > 5) {
//Debug.Log($"{this.name} goes to sleep, stale = {this.stale}");
_outputValue = Vector3.zero;
}
}
public void ProcessStimulus(int thingId, Vector3 inputValue, string thingName = null) {
this.array.ProcessStimulus(thingId, inputValue, thingName);
}
#endregion Update

View File

@ -6,7 +6,6 @@ using UnityEngine;
public class NucleusArray {
[SerializeReference]
private IReceptor[] _nuclei;
//private ClusterPrefab[] _clusters;
public IReceptor[] nuclei {
get {
return _nuclei;
@ -18,13 +17,10 @@ public class NucleusArray {
this.name = nucleus.name;
this._nuclei = new INucleus[1];
this._nuclei[0] = nucleus;
//this._clusters = new ClusterPrefab[0];
}
public NucleusArray(ClusterPrefab cluster) {
this.name = cluster.name;
this._nuclei = new INucleus[0];
// this._clusters = new ClusterPrefab[1];
// this._clusters[0] = cluster;
}
public NucleusArray(int size, string name) {
this.name = name;
@ -64,5 +60,60 @@ public class NucleusArray {
this._nuclei = newPerceptei;
}
public Dictionary<int, INucleus> thingReceivers = new();
public virtual void ProcessStimulus(int thingId, Vector3 inputValue, string thingName = null) {
CleanupReceivers();
if (!thingReceivers.TryGetValue(thingId, out INucleus selectedReceiver)) {
Debug.Log($"No receiver found for {thingId}");
foreach (IReceptor receptor in this.nuclei) {
if (receptor is not INucleus receiver)
continue;
if (thingReceivers.ContainsValue(receiver) == false) {
// receiver is not used yet
Debug.Log($"{thingId} -> {receiver.name}");
thingReceivers.Add(thingId, receiver);
selectedReceiver = receiver;
break;
}
}
}
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)
selectedNucleus.ProcessStimulus(inputValue);
selectedReceiver.parent.UpdateStateIsolated();
}
private void CleanupReceivers() {
// Remove a thing-receiver connection when the nucleus is inactive
List<int> receiversToRemove = new();
foreach (KeyValuePair<int, INucleus> item in thingReceivers) {
if (item.Value.isSleeping) {
Nucleus n = item.Value as Nucleus;
Debug.Log($"{item.Value.name} is sleeping, stale = {n.stale}");
receiversToRemove.Add(item.Key);
}
}
foreach (int thingId in receiversToRemove) {
INucleus selectedReceiver = thingReceivers[thingId];
thingReceivers.Remove(thingId);
Debug.Log($"Cleanup receiver for {thingId}");
int colonPos = selectedReceiver.name.IndexOf(":");
if (colonPos > 0)
selectedReceiver.name = selectedReceiver.name[..colonPos];
}
}
}

View File

@ -6,7 +6,6 @@ using static Unity.Mathematics.math;
public class Receptor : IReceptor {
[SerializeField]
protected string _name;
public virtual string name {
@ -16,13 +15,7 @@ public class Receptor : IReceptor {
public Receptor(Cluster parent) {
this.parent = parent;
if (parent != null)
parent.nuclei.Add(this);
}
public Receptor(ClusterPrefab cluster) {
this.cluster = cluster;
if (cluster != null)
cluster.nuclei.Add(this);
parent?.nuclei.Add(this);
}
public Receptor(Cluster parent, string name, string nucleusName) {
@ -37,8 +30,7 @@ public class Receptor : IReceptor {
}
}
private ClusterPrefab cluster;
private Cluster parent;
private readonly Cluster parent;
public virtual IReceptor ShallowCloneTo(Cluster parent) {
Receptor clone = new(parent);
@ -46,7 +38,7 @@ public class Receptor : IReceptor {
}
public virtual IReceptor Clone() {
Receptor clone = new(this.cluster);
Receptor clone = new(this.parent);
foreach (INucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
@ -90,9 +82,6 @@ public class Receptor : IReceptor {
private bool _isSleeping = false;
public bool isSleeping => _isSleeping;
// public float distanceResolution = 0.1f;
// public float directionResolution = 5;
private float3 _outputValue;
public float3 outputValue {
get { return this._outputValue; }

85
ReceptorArray.cs Normal file
View File

@ -0,0 +1,85 @@
using System.Collections.Generic;
using UnityEngine;
public class ReceptorArray {
public string name;
public Cluster parent { get; set; }
public NucleusArray nuclei;
public int size => nuclei.nuclei.Length;
public Dictionary<int, INucleus> thingReceivers = new();
public ReceptorArray(Cluster parent, NucleusArray nuclei) {
this.parent = parent;
this.nuclei = nuclei;
}
public virtual void ProcessStimulus(int thingId, Vector3 newLocalPositionVector, string thingName = null) {
if (!thingReceivers.TryGetValue(thingId, out INucleus selectedReceiver)) {
foreach (IReceptor receptor in this.nuclei.nuclei) {
if (receptor is not INucleus receiver)
continue;
if (thingReceivers.ContainsValue(receiver))
continue;
// receiver is not used yet
thingReceivers.Add(thingId, receiver);
selectedReceiver = receiver;
}
}
//selectedReceiver.outputValue = newLocalPositionVector;
/*
int receiverIx = 0;
INucleus selectedReceiver = null;
int selectedReceiverIx = 0;
foreach (INucleus receiver in this.receivers) {
if (thingIds[receiverIx] == thingId) {
// We found an existing receiver for this thing
selectedReceiver = receiver;
selectedReceiverIx = receiverIx;
// Do not look any further
break;
}
else if (receiver.isSleeping) {
// A sleeping receiver is not active and can therefore always be used
selectedReceiver = receiver;
selectedReceiverIx = receiverIx;
// Look further because we may find an existing receiver for this thing
}
else if (selectedReceiver == null) {
// If we haven't found a receiver yet, just start by taking the first
selectedReceiver = receiver;
selectedReceiverIx = receiverIx;
}
else if (selectedReceiver.isSleeping == false) {
// If no existing or sleeping receiver is found, we look for
// the receiver with the furthest/least interesting stimulus
if (length(receiver.outputValue) < length(selectedReceiver.outputValue)) {
// Debug.Log($"{selectedReceiver.name}[{selectedReceiverIx}] {length(selectedReceiver.outputValue)}" +
// $" {receiver.name}[{receiverIx}] {length(receiver.outputValue)} ");
selectedReceiver = receiver;
selectedReceiverIx = receiverIx;
}
}
receiverIx++;
}
if (selectedReceiverIx >= thingIds.Length)
return;
thingIds[selectedReceiverIx] = thingId;
if (thingName != null) {
string baseName = selectedReceiver.name;
int colonPos = selectedReceiver.name.IndexOf(":");
if (colonPos > 0)
baseName = selectedReceiver.name.Substring(0, colonPos);
selectedReceiver.name = baseName + ": " + thingName;
}
Debug.Log($"Receiver {selectedReceiver.name}[{selectedReceiverIx}] for thing {thingId}");
selectedReceiver.parent.UpdateStateIsolated();
*/
}
}

2
ReceptorArray.cs.meta Normal file
View File

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