Cluster no longer has output

This commit is contained in:
Pascal Serrarens 2026-03-04 14:20:23 +01:00
parent 172d7ca8e8
commit 1dfe65eefa
8 changed files with 325 additions and 217 deletions

View File

@ -104,7 +104,7 @@ public class Cluster : Nucleus {
foreach (Nucleus prefabArrayNucleus in prefabReceptor.nucleiArray) {
int arrayNucleusIx = GetNucleusIndex(prefabNuclei, prefabArrayNucleus);
if (arrayNucleusIx >= 0) {
Nucleus clonedArrayNucleus = clonedNuclei[arrayNucleusIx];
Neuron clonedArrayNucleus = clonedNuclei[arrayNucleusIx] as Neuron;
clonedArray.nuclei[arrayIx] = clonedArrayNucleus;
}
else {
@ -473,12 +473,12 @@ public class Cluster : Nucleus {
Debug.Log($"Update from {startNucleus.name}");
foreach (Nucleus nucleus in computeOrder) {
nucleus.UpdateStateIsolated();
if (startNucleus.trace)
Debug.Log($" {nucleus.name}[{nucleus.GetHashCode()}] = {nucleus.outputValue}");
if (startNucleus.trace && nucleus is Neuron neuron)
Debug.Log($" {nucleus.name}[{nucleus.GetHashCode()}] = {neuron.outputValue}");
}
this.outputValue = this.defaultOutput.outputValue;
this.stale = 0;
// this.outputValue = this.defaultOutput.outputValue;
// this.stale = 0;
// continue in parent
this.parent?.UpdateFromNucleus(this);
@ -491,17 +491,19 @@ public class Cluster : Nucleus {
//Applying the weight factors
foreach (Synapse synapse in this.synapses) {
if (lengthsq(synapse.nucleus.outputValue) > 0) {
sum += synapse.weight * synapse.nucleus.outputValue;
this.stale = 0;
if (synapse.nucleus is Neuron neuron) {
if (lengthsq(neuron.outputValue) > 0) {
sum += synapse.weight * neuron.outputValue;
//this.stale = 0;
}
}
}
foreach (Nucleus nucleus in this.sortedNuclei)
nucleus.UpdateStateIsolated();
this.outputValue = this.defaultOutput.outputValue;
this.stale = 0;
// this.outputValue = this.defaultOutput.outputValue;
// this.stale = 0;
UpdateNuclei();
}

View File

@ -100,6 +100,8 @@ public class ClusterReceptor : Cluster, IReceptor {
set { _array.nuclei = value; }
}
//public ClusterReceptor[] nucleiArray;
public void AddReceptorElement(ClusterPrefab prefab) {
IReceptorHelpers.AddReceptorElement(this, prefab);
}
@ -116,7 +118,7 @@ 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)
@ -129,30 +131,31 @@ public class ClusterReceptor : Cluster, IReceptor {
}
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);
}
// 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);
//this._array ??= new NucleusArray(this.parent);
//this._array.ProcessStimulus(thingId, inputValue, thingName);
Debug.LogError("Process Stimulus was called on clusterreceptor without a neuron specified");
}
private Dictionary<int, ClusterReceptor> thingReceivers = new();
private readonly Dictionary<int, ClusterReceptor> thingReceivers = new();
public virtual void ProcessStimulus(Neuron input, Vector3 inputValue, int thingId = 0, string thingName = null) {
CleanupReceivers();
inputValue = input.Activator(inputValue);
//inputValue = input.Activator(inputValue);
if (!thingReceivers.TryGetValue(thingId, out ClusterReceptor selectedReceiver))
selectedReceiver = FindReceiver(thingId, inputValue);
selectedReceiver = FindReceiver2(thingId, inputValue);
if (selectedReceiver == null)
return;
@ -172,18 +175,61 @@ public class ClusterReceptor : Cluster, IReceptor {
selectedNeuron.ProcessStimulusDirect(inputValue);
}
private ClusterReceptor FindReceiver(int thingId, float3 inputValue) {
// 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) {
// No existing nucleus for this thing
float inputMagnitude = length(inputValue);
//float inputMagnitude = length(inputValue);
ClusterReceptor selectedReceiver = null;
float selectedMagnitude = 0;
foreach (ClusterReceptor receiver in nucleiArray.Cast<ClusterReceptor>()) {
foreach (ClusterReceptor receiver in this.nucleiArray.Cast<ClusterReceptor>()) {
if (thingReceivers.ContainsValue(receiver) == false) {
// We found an unusued receiver
thingReceivers.Add(thingId, receiver);
return receiver;
}
else if (receiver.isSleeping) {
else if (receiver.defaultOutput.isSleeping) {
// A sleeping receiver is not active and can therefore always be used
thingReceivers.Add(thingId, receiver);
return receiver;
@ -191,15 +237,15 @@ public class ClusterReceptor : Cluster, IReceptor {
else if (selectedReceiver == null) {
// If we haven't found a receiver yet, just start by taking the first
selectedReceiver = receiver;
selectedMagnitude = length(selectedReceiver.outputValue);
selectedMagnitude = length(selectedReceiver.defaultOutput.outputValue);
}
// Look for the receiver with the lowest magnitude
// Look for the receiver with the lowest output magnitude
else {
float magnitude = length(receiver.outputValue);
float magnitude = length(receiver.defaultOutput.outputValue);
if (magnitude < inputMagnitude && length(receiver.outputValue) < selectedMagnitude) {
if (length(receiver.defaultOutput.outputValue) < selectedMagnitude) {
selectedReceiver = receiver;
selectedMagnitude = length(selectedReceiver.outputValue);
selectedMagnitude = length(selectedReceiver.defaultOutput.outputValue);
}
}
}
@ -215,13 +261,13 @@ public class ClusterReceptor : Cluster, IReceptor {
return selectedReceiver;
}
private void CleanupReceivers() {
// Remove a thing-receiver connection when the nucleus is inactive
List<int> receiversToRemove = new();
foreach (KeyValuePair<int, ClusterReceptor> item in thingReceivers) {
if (item.Value != null && item.Value.isSleeping)
receiversToRemove.Add(item.Key);
if (item.Value != null && item.Value.defaultOutput.isSleeping)
receiversToRemove.Add(item.Key);
}
foreach (int thingId in receiversToRemove) {
Nucleus selectedReceiver = thingReceivers[thingId];

View File

@ -262,9 +262,11 @@ public class ClusterInspector : Editor {
if (this.currentNucleus is IReceptor receptor1) {
float maxValue = 0;
foreach (Nucleus nucleus in receptor1.nucleiArray) {
float value = length(nucleus.outputValue);
if (value > maxValue)
maxValue = value;
if (nucleus is Neuron neuron) {
float value = length(neuron.outputValue);
if (value > maxValue)
maxValue = value;
}
}
float spacing = 400f / receptor1.nucleiArray.Count();
@ -309,7 +311,13 @@ public class ClusterInspector : Editor {
Handles.color = Color.white;
// The selected nucleus highlight ring
Handles.DrawSolidDisc(position, Vector3.forward, size + 2);
DrawNucleus(this.currentNucleus, position, length(this.currentNucleus.outputValue), 20);
float maxValue = 1;
if (this.currentNucleus is Neuron neuron)
maxValue = length(neuron.outputValue);
else if (this.currentNucleus is Cluster cluster)
maxValue = length(cluster.defaultOutput.outputValue);
DrawNucleus(this.currentNucleus, position, maxValue, 20);
}
}
@ -317,7 +325,12 @@ public class ClusterInspector : Editor {
Handles.color = Color.white;
// The selected nucleus highlight ring
Handles.DrawSolidDisc(position, Vector3.forward, size + 2);
DrawNucleus(this.currentNucleus, position, length(this.currentNucleus.outputValue), 20);
float maxValue = 1;
if (this.currentNucleus is Neuron neuron)
maxValue = length(neuron.outputValue);
else if (this.currentNucleus is Cluster cluster)
maxValue = length(cluster.defaultOutput.outputValue);
DrawNucleus(this.currentNucleus, position, maxValue, 20);
}
}
@ -388,10 +401,12 @@ public class ClusterInspector : Editor {
continue;
drawnArrays.Add(clusterReceptor.nucleiArray);
}
float value = length(synapse.nucleus.outputValue) * synapse.weight;
// Debug.Log($"{synapse.nucleus.name}: {value} {length(synapse.nucleus.outputValue)} {synapse.weight}");
if (value > maxValue)
maxValue = value;
if (synapse.nucleus is Neuron synapseNeuron) {
float value = length(synapseNeuron.outputValue) * synapse.weight;
// Debug.Log($"{synapse.nucleus.name}: {value} {length(synapse.nucleus.outputValue)} {synapse.weight}");
if (value > maxValue)
maxValue = value;
}
neuronCount++;
}
@ -419,7 +434,9 @@ public class ClusterInspector : Editor {
if (Application.isPlaying) {
if (maxValue == 0 || !float.IsFinite(maxValue))
maxValue = 1;
float brightness = length(synapse.nucleus.outputValue * synapse.weight) / maxValue;
float brightness = 0;
if (synapse.nucleus is Neuron synapseNeuron)
brightness = length(synapseNeuron.outputValue * synapse.weight) / maxValue;
color = new Color(brightness, brightness, brightness, 1f);
}
if (synapse.nucleus.parent != null && synapse.nucleus.parent != this.currentNucleus.parent) {
@ -439,7 +456,9 @@ public class ClusterInspector : Editor {
private void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue, float size) {
Color color;
if (Application.isPlaying) {
float brightness = length(nucleus.outputValue) / maxValue;
float brightness = 0;
if (nucleus is Neuron neuron)
brightness = length(neuron.outputValue) / maxValue;
color = new Color(brightness, brightness, brightness, 1f);
}
else
@ -529,9 +548,13 @@ public class ClusterInspector : Editor {
private void HandleMouseHover(Nucleus nucleus, Rect rect) {
GUIContent tooltip;
tooltip = new(
$"{nucleus.name}" +
$"\nValue: {length(nucleus.outputValue)}");
if (nucleus is Neuron neuron) {
tooltip = new(
$"{nucleus.name}" +
$"\nValue: {length(neuron.outputValue)}");
}
else
tooltip = new($"{nucleus.name}");
Vector2 mousePosition = Event.current.mousePosition;
@ -609,8 +632,12 @@ public class ClusterInspector : Editor {
}
if (Application.isPlaying) {
GUIContent nameLabel = new("Output", this.currentNucleus.outputValue.ToString());
EditorGUILayout.FloatField(nameLabel, length(this.currentNucleus.outputValue));
if (currentNucleus is Neuron currentNeuron1) {
GUIContent nameLabel = new("Output", currentNeuron1.outputValue.ToString());
EditorGUILayout.FloatField(nameLabel, length(currentNeuron1.outputValue));
}
else
EditorGUILayout.LabelField(" ");
}
else
EditorGUILayout.LabelField(" ");
@ -686,9 +713,11 @@ public class ClusterInspector : Editor {
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));
if (synapse.nucleus is Neuron synapseNeuron) {
Vector3 value = synapseNeuron.outputValue * synapse.weight;
GUIContent synapseValueLabel = new(synapse.nucleus.name, synapseNeuron.outputValue.ToString());
EditorGUILayout.FloatField(synapseValueLabel, length(synapseNeuron.outputValue));
}
}
else {
EditorGUILayout.BeginHorizontal();
@ -784,8 +813,8 @@ public class ClusterInspector : Editor {
EditorGUILayout.Space();
breakOnWake = EditorGUILayout.Toggle("Break on wake", breakOnWake);
if (breakOnWake) {
if (this.currentNucleus.isSleeping == false)
if (breakOnWake && this.currentNucleus is Neuron currentNeuron) {
if (currentNeuron.isSleeping == false)
Debug.Break();
}
trace = EditorGUILayout.Toggle("Trace", trace);
@ -802,15 +831,19 @@ public class ClusterInspector : Editor {
if (this.gameObject != null) {
if (this.currentNucleus is IReceptor receptor) {
foreach (Nucleus nucleus in receptor.nucleiArray) {
Vector3 worldVector = this.gameObject.transform.TransformVector(nucleus.outputValue);
Handles.color = Color.yellow;
Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector);
if (nucleus is Neuron neuron) {
Vector3 worldVector = this.gameObject.transform.TransformVector(neuron.outputValue);
Handles.color = Color.yellow;
Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector);
}
}
}
else {
Vector3 worldVector = this.gameObject.transform.TransformVector(this.currentNucleus.outputValue);
Handles.color = Color.yellow;
Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector);
if (this.currentNucleus is Neuron currentNeuron) {
Vector3 worldVector = this.gameObject.transform.TransformVector(currentNeuron.outputValue);
Handles.color = Color.yellow;
Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector);
}
}
}
}
@ -825,15 +858,15 @@ public class ClusterInspector : Editor {
case Nucleus.Type.MemoryCell:
AddMemoryCellInput(nucleus);
break;
case Nucleus.Type.Selector:
AddSelectorInput(nucleus);
break;
// case Nucleus.Type.Selector:
// AddSelectorInput(nucleus);
// break;
case Nucleus.Type.Cluster:
AddClusterInput(nucleus);
break;
case Nucleus.Type.Pulsar:
AddPulsarInput(nucleus);
break;
// case Nucleus.Type.Pulsar:
// AddPulsarInput(nucleus);
// break;
case Nucleus.Type.Receptor:
AddReceptorInput(nucleus);
break;
@ -855,19 +888,19 @@ public class ClusterInspector : Editor {
BuildLayers();
}
protected void AddSelectorInput(Nucleus nucleus) {
Selector newSelector = new(this.prefab, "New Selector");
newSelector.AddReceiver(nucleus);
this.currentNucleus = newSelector;
BuildLayers();
}
// protected void AddSelectorInput(Nucleus nucleus) {
// Selector newSelector = new(this.prefab, "New Selector");
// newSelector.AddReceiver(nucleus);
// this.currentNucleus = newSelector;
// BuildLayers();
// }
protected void AddPulsarInput(Nucleus nucleus) {
Pulsar newPulsar = new(this.prefab, "New Pulsar");
newPulsar.AddReceiver(nucleus);
this.currentNucleus = newPulsar;
BuildLayers();
}
// protected void AddPulsarInput(Nucleus nucleus) {
// Pulsar newPulsar = new(this.prefab, "New Pulsar");
// newPulsar.AddReceiver(nucleus);
// this.currentNucleus = newPulsar;
// BuildLayers();
// }
protected virtual void AddMemoryCellInput(Nucleus nucleus) {
MemoryCell newMemory = new(this.prefab, "New memory cell");

View File

@ -117,6 +117,23 @@ public class Neuron : Nucleus {
#endregion Serialization
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;
// this clone the nucleus without the synapses and receivers
public override Nucleus ShallowCloneTo(Cluster newParent) {
Neuron clone = new(newParent, this.name);
@ -164,7 +181,8 @@ public class Neuron : Nucleus {
if (receiver != null && receiver.synapses != null)
receiver.synapses.RemoveAll(s => s.nucleus == nucleus);
}
} else if (nucleus is Cluster cluster) {
}
else if (nucleus is Cluster cluster) {
// remove all receivers for this cluster
foreach (Neuron output in cluster.outputs) {
foreach (Nucleus receiver in output.receivers) {
@ -197,15 +215,19 @@ public class Neuron : Nucleus {
public float3 CombinatorSum() {
float3 sum = this.bias;
foreach (Synapse synapse in this.synapses)
sum += synapse.weight * synapse.nucleus.outputValue;
foreach (Synapse synapse in this.synapses) {
if (synapse.nucleus is Neuron neuron)
sum += synapse.weight * neuron.outputValue;
}
return sum;
}
public float3 CombinatorProduct() {
float3 product = this.bias;
foreach (Synapse synapse in this.synapses)
product *= synapse.weight * synapse.nucleus.outputValue;
foreach (Synapse synapse in this.synapses) {
if (synapse.nucleus is Neuron neuron)
product *= synapse.weight * neuron.outputValue;
}
return product;
}
@ -215,12 +237,14 @@ public class Neuron : Nucleus {
//Applying the weight factors
foreach (Synapse synapse in this.synapses) {
float3 input = synapse.weight * synapse.nucleus.outputValue;
if (synapse.nucleus is Neuron neuron) {
float3 input = synapse.weight * neuron.outputValue;
float inputLength = length(input);
if (inputLength > maxLength) {
max = input;
maxLength = inputLength;
float inputLength = length(input);
if (inputLength > maxLength) {
max = input;
maxLength = inputLength;
}
}
}
return max;
@ -278,7 +302,7 @@ public class Neuron : Nucleus {
set { _receivers = value; }
}
public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) {
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) {
@ -288,8 +312,8 @@ public class Neuron : Nucleus {
// }
// }
// else {
this._receivers.Add(receiverToAdd);
receiverToAdd.AddSynapse(this, weight);
this._receivers.Add(receiverToAdd);
receiverToAdd.AddSynapse(this, weight);
// }
}
@ -313,8 +337,9 @@ public class Neuron : Nucleus {
public override void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null) {
if (this.parent is ClusterReceptor clusterReceptor) {
clusterReceptor.ProcessStimulus(this, inputValue, thingId, thingName);
} else
clusterReceptor.ProcessStimulus(this, inputValue, thingId, thingName);
}
else
ProcessStimulusDirect(inputValue, thingId, thingName);
// this.stale = 0;
// this.bias = inputValue;

View File

@ -13,22 +13,22 @@ 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;
// 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 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);
@ -81,7 +81,7 @@ public abstract class Nucleus {
}
public virtual void SetBias(Vector3 inputValue) {
this.stale = 0;
//this.stale = 0;
this.bias = inputValue;
this.parent.UpdateFromNucleus(this);
}

View File

@ -69,9 +69,11 @@ public class NucleusArray {
private Nucleus FindReceiver(int thingId, float3 inputValue) {
// No existing nucleus for this thing
float inputMagnitude = length(inputValue);
Nucleus selectedReceiver = null;
Neuron selectedReceiver = null;
float selectedMagnitude = 0;
foreach (Nucleus receiver in this._nuclei) {
foreach (Nucleus nucleusReceiver in this._nuclei) {
if (nucleusReceiver is not Neuron receiver)
continue;
if (thingReceivers.ContainsValue(receiver) == false) {
// We found an unusued receiver
thingReceivers.Add(thingId, receiver);
@ -138,7 +140,7 @@ public class NucleusArray {
// Remove a thing-receiver connection when the nucleus is inactive
List<int> receiversToRemove = new();
foreach (KeyValuePair<int, Nucleus> item in thingReceivers) {
if (item.Value != null && item.Value.isSleeping)
if (item.Value != null && item.Value is Neuron neuron && neuron.isSleeping)
receiversToRemove.Add(item.Key);
}
foreach (int thingId in receiversToRemove) {

View File

@ -1,54 +1,54 @@
using System;
using Unity.Mathematics;
// using System;
// using Unity.Mathematics;
/// <summary>
/// The Pulsar represents a type of neuron that operates based on
/// the product of its weighted inputs rather than the traditional summation.
/// Drawing inspiration from the concept of pulsars in astrophysics
/// —highly magnetized rotating neutron stars that emit beams of radiation—
/// the Pulsar could symbolize dynamic, focused output based on the interaction of multiple factors.
/// </summary>
/// Multiplicative Functionality:
/// Instead of summing inputs, the Pulsar takes the weighted product of its inputs.
/// This means that all inputs must be active (non-zero) for the neuron to "pulse" or activate.
/// Output Behavior:
/// The output could amplify or diminish depending on the magnitude of the inputs.
/// The product would be sensitive to small values,
/// which means that even a small input could significantly lower the overall output if multiplied.
/// Activation Mechanism:
/// The activation function can further refine the output from the product result.
/// For instance, a certain threshold could be used to determine if a pulse occurs.
/// Modeling Complex Interactions:
/// The Pulsar could be particularly beneficial for modeling situations where interactions multiply rather than add.
/// This is useful in fields such as economics (e.g., compound growth models),
/// biology (e.g., interaction of hormones), and machine learning where multiplicative relationships exist.
[Serializable]
public class Pulsar : Neuron {
public Pulsar(Cluster parent, string name) : base(parent, name) {
// To prevent mistakes, bias one (instead of zero for standard neurons)
this.bias = new float3(1, 1, 1);
}
public Pulsar(ClusterPrefab parent, string name) : base(parent, name) {
// To prevent mistakes, bias one (instead of zero for standard neurons)
this.bias = new float3(1, 1, 1);
}
// /// <summary>
// /// The Pulsar represents a type of neuron that operates based on
// /// the product of its weighted inputs rather than the traditional summation.
// /// Drawing inspiration from the concept of pulsars in astrophysics
// /// —highly magnetized rotating neutron stars that emit beams of radiation—
// /// the Pulsar could symbolize dynamic, focused output based on the interaction of multiple factors.
// /// </summary>
// /// Multiplicative Functionality:
// /// Instead of summing inputs, the Pulsar takes the weighted product of its inputs.
// /// This means that all inputs must be active (non-zero) for the neuron to "pulse" or activate.
// /// Output Behavior:
// /// The output could amplify or diminish depending on the magnitude of the inputs.
// /// The product would be sensitive to small values,
// /// which means that even a small input could significantly lower the overall output if multiplied.
// /// Activation Mechanism:
// /// The activation function can further refine the output from the product result.
// /// For instance, a certain threshold could be used to determine if a pulse occurs.
// /// Modeling Complex Interactions:
// /// The Pulsar could be particularly beneficial for modeling situations where interactions multiply rather than add.
// /// This is useful in fields such as economics (e.g., compound growth models),
// /// biology (e.g., interaction of hormones), and machine learning where multiplicative relationships exist.
// [Serializable]
// public class Pulsar : Neuron {
// public Pulsar(Cluster parent, string name) : base(parent, name) {
// // To prevent mistakes, bias one (instead of zero for standard neurons)
// this.bias = new float3(1, 1, 1);
// }
// public Pulsar(ClusterPrefab parent, string name) : base(parent, name) {
// // To prevent mistakes, bias one (instead of zero for standard neurons)
// this.bias = new float3(1, 1, 1);
// }
public override Nucleus ShallowCloneTo(Cluster newParent) {
Pulsar clone = new(newParent, this.name);
CloneFields(clone);
return clone;
}
// public override Nucleus ShallowCloneTo(Cluster newParent) {
// Pulsar clone = new(newParent, this.name);
// CloneFields(clone);
// return clone;
// }
public override void UpdateStateIsolated() {
float3 product = this.bias;
// public override void UpdateStateIsolated() {
// float3 product = this.bias;
//Applying the weight factors
foreach (Synapse synapse in this.synapses) {
float3 input = synapse.weight * synapse.nucleus.outputValue;
product *= input;
}
// //Applying the weight factors
// foreach (Synapse synapse in this.synapses) {
// float3 input = synapse.weight * synapse.nucleus.outputValue;
// product *= input;
// }
// Activation function
this.outputValue = Activator(product);
}
}
// // Activation function
// this.outputValue = Activator(product);
// }
// }

View File

@ -1,62 +1,62 @@
using System;
using Unity.Mathematics;
using static Unity.Mathematics.math;
// using System;
// using Unity.Mathematics;
// using static Unity.Mathematics.math;
[Serializable]
public class Selector : Neuron {
public Selector(Cluster parent, string name) : base(parent, name) { }
public Selector(ClusterPrefab parent, string name) : base(parent, name) {}
// [Serializable]
// public class Selector : Neuron {
// public Selector(Cluster parent, string name) : base(parent, name) { }
// public Selector(ClusterPrefab parent, string name) : base(parent, name) {}
public override Nucleus ShallowCloneTo(Cluster newParent) {
Selector clone = new(newParent, this.name) {
// array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
curveMax = this.curveMax,
};
return clone;
}
// public override Nucleus ShallowCloneTo(Cluster newParent) {
// Selector clone = new(newParent, this.name) {
// // array = this.array,
// curve = this.curve,
// curvePreset = this.curvePreset,
// curveMax = this.curveMax,
// };
// return clone;
// }
public override void UpdateStateIsolated() { //float3 bias) {
float3 max = this.bias;
float maxSqrLength = lengthsq(max);
// public override void UpdateStateIsolated() { //float3 bias) {
// float3 max = this.bias;
// float maxSqrLength = lengthsq(max);
//Applying the weight factors
foreach (Synapse synapse in this.synapses) {
float3 input = synapse.weight * synapse.nucleus.outputValue;
// //Applying the weight factors
// foreach (Synapse synapse in this.synapses) {
// float3 input = synapse.weight * synapse.nucleus.outputValue;
float inputSqrlength = lengthsq(input);
if (inputSqrlength > maxSqrLength) {
max = input;
maxSqrLength = inputSqrlength;
}
}
// float inputSqrlength = lengthsq(input);
// if (inputSqrlength > maxSqrLength) {
// max = input;
// maxSqrLength = inputSqrlength;
// }
// }
// Activation function
float3 result;
switch (this.curvePreset) {
case CurvePresets.Linear:
result = max;
break;
case CurvePresets.Sqrt:
result = normalize(max) * System.MathF.Sqrt(length(max));
break;
case CurvePresets.Power:
result = normalize(max) * System.MathF.Pow(length(max), 2);
break;
case CurvePresets.Reciprocal: {
float magnitude = length(max);
if (magnitude > 0)
result = normalize(max) * (1 / magnitude);
else
result = float3(0, 0, 0);
break;
}
default:
float activatedValue = this.curve.Evaluate(length(max));
result = normalize(max) * activatedValue;
break;
}
this.outputValue = result;
}
}
// // Activation function
// float3 result;
// switch (this.curvePreset) {
// case CurvePresets.Linear:
// result = max;
// break;
// case CurvePresets.Sqrt:
// result = normalize(max) * System.MathF.Sqrt(length(max));
// break;
// case CurvePresets.Power:
// result = normalize(max) * System.MathF.Pow(length(max), 2);
// break;
// case CurvePresets.Reciprocal: {
// float magnitude = length(max);
// if (magnitude > 0)
// result = normalize(max) * (1 / magnitude);
// else
// result = float3(0, 0, 0);
// break;
// }
// default:
// float activatedValue = this.curve.Evaluate(length(max));
// result = normalize(max) * activatedValue;
// break;
// }
// this.outputValue = result;
// }
// }