Pulsar neuron and firing actions

This commit is contained in:
Pascal Serrarens 2026-02-10 11:47:49 +01:00
parent fd7359acd0
commit 6f67def8e6
5 changed files with 155 additions and 44 deletions

View File

@ -678,24 +678,46 @@ public class ClusterInspector : Editor {
}
}
protected virtual void AddInput(Nucleus.Type selectedType, Nucleus nucleus) {
switch (selectedType) {
case Nucleus.Type.Neuron:
AddNeuronInput(nucleus);
break;
case Nucleus.Type.MemoryCell:
AddMemoryCellInput(nucleus);
break;
case Nucleus.Type.Selector:
AddSelectorInput(nucleus);
break;
case Nucleus.Type.Cluster:
AddClusterInput(nucleus);
break;
case Nucleus.Type.Pulsar:
AddPulsarInput(nucleus);
break;
default:
break;
}
}
protected virtual void AddInput(int selectedInputType, Nucleus nucleus) {
switch (selectedInputType) {
case 0: // Neuron
AddInputNeuron(nucleus);
AddNeuronInput(nucleus);
break;
case 1: // MemoryCell
AddInputMemoryCell(nucleus);
AddMemoryCellInput(nucleus);
break;
case 2: // Selector
AddSelectorInput(nucleus);
break;
case 3: // Cluster
AddCluster(nucleus);
AddClusterInput(nucleus);
break;
}
}
protected virtual void AddInputNeuron(Nucleus nucleus) {
protected virtual void AddNeuronInput(Nucleus nucleus) {
//Neuron newNeuroid = new(this.cluster, "New neuron");
Neuron newNeuroid = new(this.prefab, "New neuron");
newNeuroid.AddReceiver(nucleus);
@ -725,14 +747,21 @@ public class ClusterInspector : Editor {
BuildLayers();
}
protected virtual void AddInputMemoryCell(Nucleus nucleus) {
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");
newMemory.AddReceiver(nucleus);
this.currentNucleus = newMemory;
BuildLayers();
}
protected virtual void AddCluster(Nucleus nucleus) {
protected virtual void AddClusterInput(Nucleus nucleus) {
ClusterPickerWindow.ShowPicker(brain => OnClusterPicked(nucleus, brain), "Select Cluster");
}
@ -777,11 +806,13 @@ public class ClusterInspector : Editor {
if (cluster == null)
return;
string[] options = { "Neuron", "MemoryCell", "Selector", "Cluster" };
int selectedInputType = -1;
selectedInputType = EditorGUILayout.Popup("Add", selectedInputType, options);
if (selectedInputType >= 0)
AddInput(selectedInputType, this.currentNucleus);
//string[] options = { "Neuron", "MemoryCell", "Selector", "Cluster" };
//int selectedInputType = -1;
//selectedInputType = EditorGUILayout.Popup("Add", selectedInputType, options);
//if (selectedInputType >= 0)
Nucleus.Type selectedType = (Nucleus.Type)EditorGUILayout.EnumPopup("Add", Nucleus.Type.None);
if (selectedType != Nucleus.Type.None)
AddInput(selectedType, this.currentNucleus);
}
protected virtual void DisconnectNucleus(Neuron nucleus) {

View File

@ -129,29 +129,33 @@ public class Neuron : Nucleus {
// this clone the nucleus without the synapses and receivers
public override Nucleus ShallowCloneTo(Cluster newParent) {
Neuron clone = new(newParent, this.name) {
array = null,
bias = this.bias,
curve = this.curve,
curvePreset = this.curvePreset,
curveMax = this.curveMax,
average = this.average
};
Neuron clone = new(newParent, this.name);
// {
// array = null,
// bias = this.bias,
// curve = this.curve,
// curvePreset = this.curvePreset,
// curveMax = this.curveMax,
// average = this.average
// };
CloneFields(clone);
return clone;
}
public override Nucleus Clone(ClusterPrefab prefab) {
Neuron clone = new(prefab, this.name) {
//Neuron clone = new(this.parent, this.name) {
array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
curveMax = this.curveMax,
average = this.average
};
Neuron clone = new(prefab, this.name);
// {
// //Neuron clone = new(this.parent, this.name) {
// array = this.array,
// curve = this.curve,
// curvePreset = this.curvePreset,
// curveMax = this.curveMax,
// average = this.average
// };
// if (clone.cluster != null)
// clone.cluster.nuclei.Add(clone);
CloneFields(clone);
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
@ -162,6 +166,15 @@ public class Neuron : Nucleus {
return clone;
}
protected virtual void CloneFields(Neuron clone) {
clone.array = null;
clone.bias = this.bias;
clone.curve = this.curve;
clone.curvePreset = this.curvePreset;
clone.curveMax = this.curveMax;
clone.average = this.average;
}
public static void Delete(Nucleus nucleus) {
foreach (Synapse synapse in nucleus.synapses) {
if (synapse.nucleus is Neuron synapse_nucleus) {
@ -205,32 +218,37 @@ public class Neuron : Nucleus {
sum /= n;
// Activation function
float3 result = Activation(sum);
if (this.stale > staleValueForSleep)
this.outputValue = new float3(0, 0, 0);
else
this.outputValue = result;
}
protected float3 Activation(float3 input) {
float3 result = Vector3.zero;
switch (this.curvePreset) {
case CurvePresets.Linear:
result = sum;
result = input;
break;
case CurvePresets.Sqrt:
result = normalize(sum) * System.MathF.Sqrt(length(sum));
result = normalize(input) * System.MathF.Sqrt(length(input));
break;
case CurvePresets.Power:
result = normalize(sum) * System.MathF.Pow(length(sum), 2);
result = normalize(input) * System.MathF.Pow(length(input), 2);
break;
case CurvePresets.Reciprocal: {
float magnitude = length(sum);
float magnitude = length(input);
if (magnitude > 0)
result = normalize(sum) * (1 / magnitude);
result = normalize(input) * (1 / magnitude);
break;
}
default:
float activatedValue = this.curve.Evaluate(length(sum));
result = normalize(sum) * activatedValue;
float activatedValue = this.curve.Evaluate(length(input));
result = normalize(input) * activatedValue;
break;
}
if (this.stale > staleValueForSleep)
this.outputValue = new float3(0,0,0);
else
this.outputValue = result;
return result;
}
public virtual void ProcessStimulus(Vector3 inputValue, string thingName = null) {

View File

@ -6,12 +6,6 @@ using static Unity.Mathematics.math;
[Serializable]
public abstract class Nucleus {
// [SerializeField]
// protected string _name;
// public virtual string name {
// get => _name;
// set => _name = value;
// }
public string name;
//[Obsolete]
@ -26,9 +20,12 @@ public abstract class Nucleus {
//this.stale = 0;
// this._isSleeping = false;
_outputValue = value;
if (this.isFiring)
WhenFiring?.Invoke();
}
}
public bool isFiring => length(_outputValue) > 0.5f;
public Action WhenFiring;
public bool isSleeping => lengthsq(this.outputValue) == 0;
[NonSerialized]
@ -38,6 +35,15 @@ public abstract class Nucleus {
public abstract Nucleus ShallowCloneTo(Cluster parent);
public abstract Nucleus Clone(ClusterPrefab prefab);
public enum Type {
None,
Neuron,
MemoryCell,
Selector,
Cluster,
Pulsar
}
#region Synapses
public Vector3 bias = Vector3.zero;

54
Pulsar.cs Normal file
View File

@ -0,0 +1,54 @@
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);
}
public override Nucleus ShallowCloneTo(Cluster newParent) {
Pulsar clone = new(newParent, this.name);
CloneFields(clone);
return clone;
}
public override void UpdateStateIsolated(float3 _bias) {
float3 product = this.bias;
//Applying the weight factors
foreach (Synapse synapse in this.synapses) {
float3 input = synapse.weight * synapse.nucleus.outputValue;
product *= input;
}
// Activation function
this.outputValue = Activation(product);
}
}

2
Pulsar.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 46bd155173053a01585411c3e07f85d4