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

View File

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

View File

@ -193,7 +193,8 @@ public class Neuron : Nucleus, INucleus {
} }
public override IReceptor Clone() { 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, array = this.array,
curve = this.curve, curve = this.curve,
curvePreset = this.curvePreset, curvePreset = this.curvePreset,
@ -299,8 +300,10 @@ public class Neuron : Nucleus, INucleus {
// public virtual void UpdateStateIsolated() { // public virtual void UpdateStateIsolated() {
// UpdateStateIsolated(new float3(0, 0, 0)); // 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; int n = 0;
//Applying the weight factgors //Applying the weight factgors
@ -308,8 +311,10 @@ public class Neuron : Nucleus, INucleus {
sum += synapse.weight * synapse.nucleus.outputValue; sum += synapse.weight * synapse.nucleus.outputValue;
// Perhaps synapses should be removed when the output value goes to 0.... // 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++; n++;
this.stale = 0;
}
} }
if (this.average && n > 0) if (this.average && n > 0)
sum /= n; sum /= n;
@ -337,9 +342,19 @@ public class Neuron : Nucleus, INucleus {
result = normalize(sum) * activatedValue; result = normalize(sum) * activatedValue;
break; break;
} }
if (this.stale > 5)
this.outputValue = new float3(0,0,0);
else
this.outputValue = result; 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) { // public virtual void UpdateResult(Vector3 result) {
// // float d = Vector3.Distance(result, this.outputValue); // // float d = Vector3.Distance(result, this.outputValue);
// // if (d < 0.5f) { // // if (d < 0.5f) {

View File

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

View File

@ -6,7 +6,6 @@ using UnityEngine;
public class NucleusArray { public class NucleusArray {
[SerializeReference] [SerializeReference]
private IReceptor[] _nuclei; private IReceptor[] _nuclei;
//private ClusterPrefab[] _clusters;
public IReceptor[] nuclei { public IReceptor[] nuclei {
get { get {
return _nuclei; return _nuclei;
@ -18,13 +17,10 @@ public class NucleusArray {
this.name = nucleus.name; this.name = nucleus.name;
this._nuclei = new INucleus[1]; this._nuclei = new INucleus[1];
this._nuclei[0] = nucleus; this._nuclei[0] = nucleus;
//this._clusters = new ClusterPrefab[0];
} }
public NucleusArray(ClusterPrefab cluster) { public NucleusArray(ClusterPrefab cluster) {
this.name = cluster.name; this.name = cluster.name;
this._nuclei = new INucleus[0]; this._nuclei = new INucleus[0];
// this._clusters = new ClusterPrefab[1];
// this._clusters[0] = cluster;
} }
public NucleusArray(int size, string name) { public NucleusArray(int size, string name) {
this.name = name; this.name = name;
@ -64,5 +60,60 @@ public class NucleusArray {
this._nuclei = newPerceptei; 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 { public class Receptor : IReceptor {
[SerializeField] [SerializeField]
protected string _name; protected string _name;
public virtual string name { public virtual string name {
@ -16,13 +15,7 @@ public class Receptor : IReceptor {
public Receptor(Cluster parent) { public Receptor(Cluster parent) {
this.parent = parent; this.parent = parent;
if (parent != null) parent?.nuclei.Add(this);
parent.nuclei.Add(this);
}
public Receptor(ClusterPrefab cluster) {
this.cluster = cluster;
if (cluster != null)
cluster.nuclei.Add(this);
} }
public Receptor(Cluster parent, string name, string nucleusName) { public Receptor(Cluster parent, string name, string nucleusName) {
@ -37,8 +30,7 @@ public class Receptor : IReceptor {
} }
} }
private ClusterPrefab cluster; private readonly Cluster parent;
private Cluster parent;
public virtual IReceptor ShallowCloneTo(Cluster parent) { public virtual IReceptor ShallowCloneTo(Cluster parent) {
Receptor clone = new(parent); Receptor clone = new(parent);
@ -46,7 +38,7 @@ public class Receptor : IReceptor {
} }
public virtual IReceptor Clone() { public virtual IReceptor Clone() {
Receptor clone = new(this.cluster); Receptor clone = new(this.parent);
foreach (INucleus receiver in this.receivers) { foreach (INucleus receiver in this.receivers) {
clone.AddReceiver(receiver); clone.AddReceiver(receiver);
@ -90,9 +82,6 @@ public class Receptor : IReceptor {
private bool _isSleeping = false; private bool _isSleeping = false;
public bool isSleeping => _isSleeping; public bool isSleeping => _isSleeping;
// public float distanceResolution = 0.1f;
// public float directionResolution = 5;
private float3 _outputValue; private float3 _outputValue;
public float3 outputValue { public float3 outputValue {
get { return this._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