Moved activation function to after weight application

This commit is contained in:
Pascal Serrarens 2026-01-08 14:09:31 +01:00
parent b9b942760b
commit 600ecd5406
13 changed files with 94 additions and 1436 deletions

View File

@ -48,7 +48,7 @@ public class Neuroid : Nucleus {
public float exponent = 1.0f; public float exponent = 1.0f;
public Neuroid(NanoBrainObj brain, string name) : base(name) { public Neuroid(NanoBrain brain, string name) : base(name) {
this.brain = brain; this.brain = brain;
if (this.brain != null) { if (this.brain != null) {
this.brain.nuclei.Add(this); this.brain.nuclei.Add(this);
@ -75,20 +75,40 @@ public class Neuroid : Nucleus {
} }
public override void UpdateState() { public override void UpdateState() {
Vector3 result = Vector3.zero; Vector3 sum = Vector3.zero;
int n = 0; int n = 0;
//Applying the weight factgors
foreach (Synapse synapse in this.synapses) { foreach (Synapse synapse in this.synapses) {
Vector3 outputValue = synapse.nucleus.outputValue; Vector3 outputValue = synapse.nucleus.outputValue;
// As this is the activation function, it should be evaluated in the synapse.nucleus... float magnitude = synapse.weight * outputValue.magnitude;
float activatedValue = curve.Evaluate(outputValue.magnitude);
float magnitude = synapse.weight * activatedValue;
result += magnitude * outputValue.normalized; sum += magnitude * outputValue.normalized;
n++; n++;
} }
if (average) if (average)
result /= n; sum /= n;
// Activation function
Vector3 result;
switch (this.curvePreset) {
case CurvePresets.Linear:
result = sum;
break;
case CurvePresets.Sqrt:
result = sum.normalized * System.MathF.Sqrt(sum.magnitude);
break;
case CurvePresets.Power:
result = sum.normalized * System.MathF.Pow(sum.magnitude, 2);
break;
case CurvePresets.Reciprocal:
result = sum.normalized * (1 / sum.magnitude);
break;
default:
float activatedValue = this.curve.Evaluate(sum.magnitude);
result = sum.normalized * activatedValue;
break;
}
UpdateResult(result); UpdateResult(result);
} }

View File

@ -26,7 +26,7 @@ public class Nucleus {
[SerializeField] [SerializeField]
protected string nucleusType; protected string nucleusType;
public virtual void Rebuild(NanoBrainObj brain) { public virtual void Rebuild(NanoBrain brain) {
if (this.synapses != null) { if (this.synapses != null) {
foreach (Synapse synapse in synapses) foreach (Synapse synapse in synapses)
synapse.Rebuild(brain); synapse.Rebuild(brain);
@ -43,7 +43,7 @@ public class Nucleus {
} }
} }
public static Nucleus RebuildType(NanoBrainObj brain, Nucleus nucleus) { public static Nucleus RebuildType(NanoBrain brain, Nucleus nucleus) {
if (string.IsNullOrEmpty(nucleus.nucleusType) == false) { if (string.IsNullOrEmpty(nucleus.nucleusType) == false) {
Type nucleusType = Type.GetType(nucleus.nucleusType); Type nucleusType = Type.GetType(nucleus.nucleusType);
if (nucleusType != null) { if (nucleusType != null) {
@ -62,7 +62,7 @@ public class Nucleus {
#region Runtime state (not serialized) #region Runtime state (not serialized)
public NanoBrainObj brain { get; set; } public NanoBrain brain { get; set; }
private Vector3 _outputValue; private Vector3 _outputValue;
public Vector3 outputValue public Vector3 outputValue
@ -154,11 +154,11 @@ public class Nucleus {
public virtual void UpdateState() { } public virtual void UpdateState() { }
public void UpdateResult(Vector3 result) { public void UpdateResult(Vector3 result) {
// float d = Vector3.Distance(result, this.outputValue); float d = Vector3.Distance(result, this.outputValue);
// if (d < 0.1f) { if (d < 0.5f) {
// //Debug.Log($"insignificant update: {d}"); //Debug.Log($"insignificant update: {d}");
// return; return;
// } }
this.outputValue = result; this.outputValue = result;
foreach (Receiver receiver in this.receivers) foreach (Receiver receiver in this.receivers)
@ -181,8 +181,8 @@ public class Synapse {
Reciprocal, Reciprocal,
Custom Custom
} }
public CurvePresets curvePreset; // public CurvePresets curvePreset;
public AnimationCurve curve; // public AnimationCurve curve;
public float curveMax = 1.0f; public float curveMax = 1.0f;
public Synapse(Nucleus nucleus, float weight) { public Synapse(Nucleus nucleus, float weight) {
@ -191,7 +191,7 @@ public class Synapse {
this.weight = weight; this.weight = weight;
} }
public void Rebuild(NanoBrainObj brain) { public void Rebuild(NanoBrain brain) {
if (brain == null) { if (brain == null) {
return; return;
} }
@ -211,30 +211,30 @@ public class Synapse {
Debug.LogError($"Synapse deserialization error: could not find nucleus with id {this.nucleusId}"); Debug.LogError($"Synapse deserialization error: could not find nucleus with id {this.nucleusId}");
} }
public AnimationCurve GenerateCurve() { // public AnimationCurve GenerateCurve() {
switch (this.curvePreset) { // switch (this.curvePreset) {
case CurvePresets.Linear: // case CurvePresets.Linear:
this.curveMax = this.weight; // this.curveMax = this.weight;
return Presets.Linear(this.weight); // return Presets.Linear(this.weight);
case CurvePresets.Power: // case CurvePresets.Power:
this.curveMax = this.weight; // this.curveMax = this.weight;
return Presets.Power(2.0f, this.weight); // return Presets.Power(2.0f, this.weight);
case CurvePresets.Sqrt: // case CurvePresets.Sqrt:
this.curveMax = this.weight; // this.curveMax = this.weight;
return Presets.Power(0.5f, this.weight); // return Presets.Power(0.5f, this.weight);
case CurvePresets.Reciprocal: // case CurvePresets.Reciprocal:
this.curveMax = 1 / 0.01f * this.weight; // this.curveMax = 1 / 0.01f * this.weight;
return Presets.Reciprocal(this.weight); // return Presets.Reciprocal(this.weight);
default: // default:
this.curveMax = weight; // this.curveMax = weight;
return AnimationCurve.Constant(0, 1, weight); // return AnimationCurve.Constant(0, 1, weight);
} // }
} // }
public static class Presets { public static class Presets {
private const int samples = 32; private const int samples = 32;
public static AnimationCurve Linear(float weight) { public static AnimationCurve Linear(float weight) {
return AnimationCurve.Linear(0f, 0f, 1f, weight); return AnimationCurve.Linear(0f, 0f, 1000f, weight * 1000);
} }
public static AnimationCurve Power(float exponent, float weight) { public static AnimationCurve Power(float exponent, float weight) {
// build keyframes // build keyframes
@ -287,7 +287,7 @@ public class Receiver {
this.nucleusId = nucleus.id; this.nucleusId = nucleus.id;
} }
public bool Rebuild(NanoBrainObj brain) { public bool Rebuild(NanoBrain brain) {
if (brain == null) { if (brain == null) {
return false; return false;
} }

View File

@ -14,7 +14,7 @@ public class Perceptoid : Neuroid {
public int thingType; public int thingType;
public int thingId; public int thingId;
public override void Rebuild(NanoBrainObj brain) { public override void Rebuild(NanoBrain brain) {
base.Rebuild(brain); base.Rebuild(brain);
this.receptor = Receptor.GetReceptor(brain, thingType); this.receptor = Receptor.GetReceptor(brain, thingType);
this.receptor.perceptei.Add(this); this.receptor.perceptei.Add(this);
@ -48,7 +48,7 @@ public class Perceptoid : Neuroid {
#endregion Serialization #endregion Serialization
public Perceptoid(NanoBrainObj brain, int thingType, string name = "sensor") : base(name) { public Perceptoid(NanoBrain brain, int thingType, string name = "sensor") : base(name) {
this.brain = brain; this.brain = brain;
if (this.brain != null) { if (this.brain != null) {
this.brain.perceptei.Add(this); this.brain.perceptei.Add(this);
@ -80,7 +80,7 @@ public class Perceptoid : Neuroid {
} }
public static Perceptoid GetPerception(NanoBrainObj brain, int thingType = 0) { public static Perceptoid GetPerception(NanoBrain brain, int thingType = 0) {
foreach (Nucleus nucleus in brain.nuclei) { foreach (Nucleus nucleus in brain.nuclei) {
if (nucleus is Perceptoid perceptoid && (thingType == 0 || perceptoid.receptor.thingType == thingType)) if (nucleus is Perceptoid perceptoid && (thingType == 0 || perceptoid.receptor.thingType == thingType))
return perceptoid; return perceptoid;

View File

@ -22,13 +22,13 @@ public class Receptor {
public float distanceResolution = 0.1f; public float distanceResolution = 0.1f;
public float directionResolution = 5; public float directionResolution = 5;
public Receptor(NanoBrainObj brain, int thingType) { public Receptor(NanoBrain brain, int thingType) {
this.thingType = thingType; this.thingType = thingType;
//this.perceptei.Add(perceptoid); //this.perceptei.Add(perceptoid);
brain.receptors.Add(this); brain.receptors.Add(this);
} }
public static Receptor GetReceptor(NanoBrainObj brain, int thingType) { public static Receptor GetReceptor(NanoBrain brain, int thingType) {
foreach (Receptor receptor in brain.receptors) { foreach (Receptor receptor in brain.receptors) {
if (thingType == 0 || receptor.thingType == thingType) if (thingType == 0 || receptor.thingType == thingType)
return receptor; return receptor;
@ -38,9 +38,6 @@ public class Receptor {
} }
public virtual void ProcessStimulus(int thingId, Vector3 newLocalPositionVector) { public virtual void ProcessStimulus(int thingId, Vector3 newLocalPositionVector) {
//Spherical newLocalPosition = Spherical.FromVector3(newLocalPositionVector);
Vector3 previousLocalPosition = this.localPosition;
this.localPosition = newLocalPositionVector; this.localPosition = newLocalPositionVector;
Perceptoid selectedPerceptoid = null; Perceptoid selectedPerceptoid = null;
@ -50,16 +47,6 @@ public class Receptor {
selectedPerceptoid = perceptoid; selectedPerceptoid = perceptoid;
// Do not look any further // Do not look any further
// // This does not do a lot....
// float deltaDistance = newLocalPosition.distance - previousLocalPosition.distance;
// // See if the change is significant
// AngleFloat deltaDirection = Direction.UnsignedAngle(newLocalPosition.direction, previousLocalPosition.direction);
// if (deltaDistance < this.distanceResolution && deltaDirection.inDegrees < directionResolution) {
// // The difference is not significant we don't process this data.
// this.localPosition = previousLocalPosition;
// return;
// }
// This is now also handled by the UpdateState Spherical.Distance
break; break;
} }
else if (perceptoid.isSleeping) { else if (perceptoid.isSleeping) {

View File

@ -20,7 +20,7 @@ public class NanoBrainComponent_Editor : Editor {
public override VisualElement CreateInspectorGUI() { public override VisualElement CreateInspectorGUI() {
//NanoBrainComponent component = target as NanoBrainComponent; //NanoBrainComponent component = target as NanoBrainComponent;
NanoBrainObj brain = Application.isPlaying ? component.brain : component.defaultBrain; NanoBrain brain = Application.isPlaying ? component.brain : component.defaultBrain;
if (Application.isPlaying == false) if (Application.isPlaying == false)
serializedObject.Update(); serializedObject.Update();

View File

@ -11,7 +11,7 @@ public class NucleusLayer {
} }
public class NanoBrainEditor : EditorWindow { public class NanoBrainEditor : EditorWindow {
public NanoBrainObj brain; public NanoBrain brain;
public static VisualElement inspectorContainer; public static VisualElement inspectorContainer;
@ -20,7 +20,7 @@ public class NanoBrainEditor : EditorWindow {
GetWindow<NanoBrainEditor>("NanoBrain Editor"); GetWindow<NanoBrainEditor>("NanoBrain Editor");
} }
public static void Open(NanoBrainObj asset) { public static void Open(NanoBrain asset) {
NanoBrainEditor editor = GetWindow<NanoBrainEditor>("NanoBrain Editor"); NanoBrainEditor editor = GetWindow<NanoBrainEditor>("NanoBrain Editor");
editor.brain = asset; editor.brain = asset;
editor.Show(); editor.Show();
@ -69,7 +69,7 @@ public class NanoBrainEditor : EditorWindow {
} }
public class GraphBoardView : VisualElement { public class GraphBoardView : VisualElement {
NanoBrainObj brain; NanoBrain brain;
SerializedObject serializedBrain; SerializedObject serializedBrain;
Nucleus currentNucleus; Nucleus currentNucleus;
private List<NeuroidLayer> layers = new(); private List<NeuroidLayer> layers = new();
@ -99,7 +99,7 @@ public class GraphBoardView : VisualElement {
RegisterCallback<MouseUpEvent>(OnMouseUp); RegisterCallback<MouseUpEvent>(OnMouseUp);
} }
public void SetGraph(NanoBrainObj brain, Nucleus nucleus) { public void SetGraph(NanoBrain brain, Nucleus nucleus) {
this.brain = brain; this.brain = brain;
this.serializedBrain = new SerializedObject(brain); this.serializedBrain = new SerializedObject(brain);
this.currentNucleus = nucleus; this.currentNucleus = nucleus;
@ -473,9 +473,9 @@ public class GraphNodeWrapper : ScriptableObject {
public string title; public string title;
public Vector2 position; public Vector2 position;
Nucleus node; Nucleus node;
NanoBrainObj graph; // needed to write back and mark dirty NanoBrain graph; // needed to write back and mark dirty
public GraphNodeWrapper Init(Nucleus node, NanoBrainObj graphAsset) { public GraphNodeWrapper Init(Nucleus node, NanoBrain graphAsset) {
this.node = node; this.node = node;
this.graph = graphAsset; this.graph = graphAsset;
this.title = " A " + node.name; this.title = " A " + node.name;
@ -499,7 +499,7 @@ public static class OpenAssetHandler {
// Called when an asset is double-clicked or opened. // Called when an asset is double-clicked or opened.
[OnOpenAsset] [OnOpenAsset]
public static bool OpenMyScriptableObject(int instanceID, int line) { public static bool OpenMyScriptableObject(int instanceID, int line) {
NanoBrainObj obj = EditorUtility.EntityIdToObject(instanceID) as NanoBrainObj; NanoBrain obj = EditorUtility.EntityIdToObject(instanceID) as NanoBrain;
if (obj != null) { if (obj != null) {
NanoBrainEditor.Open(obj); NanoBrainEditor.Open(obj);
return true; // handled return true; // handled

View File

@ -5,7 +5,7 @@ using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
[CustomEditor(typeof(NanoBrainObj))] [CustomEditor(typeof(NanoBrain))]
public class NanoBrainInspector : Editor { public class NanoBrainInspector : Editor {
protected static VisualElement mainContainer; protected static VisualElement mainContainer;
protected static VisualElement inspectorContainer; protected static VisualElement inspectorContainer;
@ -15,7 +15,7 @@ public class NanoBrainInspector : Editor {
#region Start #region Start
public override VisualElement CreateInspectorGUI() { public override VisualElement CreateInspectorGUI() {
NanoBrainObj brain = target as NanoBrainObj; NanoBrain brain = target as NanoBrain;
serializedObject.Update(); serializedObject.Update();
@ -72,7 +72,7 @@ public class NanoBrainInspector : Editor {
} }
public class GraphView : VisualElement { public class GraphView : VisualElement {
NanoBrainObj brain; NanoBrain brain;
SerializedObject serializedBrain; SerializedObject serializedBrain;
Nucleus currentNucleus; Nucleus currentNucleus;
GameObject gameObject; GameObject gameObject;
@ -103,7 +103,7 @@ public class NanoBrainInspector : Editor {
RegisterCallback<MouseUpEvent>(OnMouseUp); RegisterCallback<MouseUpEvent>(OnMouseUp);
} }
public void SetGraph(GameObject gameObject, NanoBrainObj brain, Nucleus nucleus, VisualElement inspectorContainer) { public void SetGraph(GameObject gameObject, NanoBrain brain, Nucleus nucleus, VisualElement inspectorContainer) {
this.gameObject = gameObject; this.gameObject = gameObject;
this.brain = brain; this.brain = brain;
if (Application.isPlaying == false) if (Application.isPlaying == false)

View File

@ -1,15 +1,15 @@
using UnityEngine; using UnityEngine;
public class NanoBrainComponent : MonoBehaviour { public class NanoBrainComponent : MonoBehaviour {
public NanoBrainObj defaultBrain; public NanoBrain defaultBrain;
private NanoBrainObj brainInstance; private NanoBrain brainInstance;
public Nucleus root { public Nucleus root {
get { get {
return brainInstance.root; return brainInstance.root;
} }
} }
public NanoBrainObj brain { public NanoBrain brain {
get { get {
if (brainInstance == null && defaultBrain != null) { if (brainInstance == null && defaultBrain != null) {
brainInstance = Instantiate(defaultBrain); brainInstance = Instantiate(defaultBrain);

View File

@ -3,7 +3,7 @@ using UnityEngine;
using LinearAlgebra; using LinearAlgebra;
[CreateAssetMenu(menuName = "Passer/NanoBrain")] [CreateAssetMenu(menuName = "Passer/NanoBrain")]
public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver { public class NanoBrain : ScriptableObject, ISerializationCallbackReceiver {
public string title; public string title;
public int count; public int count;
@ -21,7 +21,7 @@ public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver {
// public Perception perception; // public Perception perception;
public NanoBrainObj() { public NanoBrain() {
this.root = new Neuroid(this, "Root"); this.root = new Neuroid(this, "Root");
// this.perception = new Perception(this); // this.perception = new Perception(this);
} }

View File

@ -375,10 +375,10 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::SwarmControl m_EditorClassIdentifier: Assembly-CSharp::SwarmControl
speed: 1 speed: 1
inertia: 0.7 inertia: 0.7
alignmentForce: 2 alignmentForce: 0.5
cohesionForce: 4 cohesionForce: 2.2
separationForce: -5 separationForce: -5
avoidanceForce: 5 avoidanceForce: 1
separationDistance: 0.3 separationDistance: 0.3
perceptionDistance: 2 perceptionDistance: 2
boundaryForce: 5 boundaryForce: 5

View File

@ -10,9 +10,9 @@ public class SwarmControl_Editor : Editor {
if (EditorGUI.EndChangeCheck()) { if (EditorGUI.EndChangeCheck()) {
SwarmControl swarmControl = (SwarmControl)target; SwarmControl swarmControl = (SwarmControl)target;
NanoBrainObj[] nanoBrains = FindObjectsByType<NanoBrainObj>(FindObjectsSortMode.None); NanoBrain[] nanoBrains = FindObjectsByType<NanoBrain>(FindObjectsSortMode.None);
foreach (NanoBrainObj brain in nanoBrains) { foreach (NanoBrain brain in nanoBrains) {
UpdateWeight(brain, "Avoidance", swarmControl.avoidanceForce); UpdateWeight(brain, "Avoidance", swarmControl.avoidanceForce);
UpdateWeight(brain, "Cohesion", swarmControl.cohesionForce); UpdateWeight(brain, "Cohesion", swarmControl.cohesionForce);
UpdateWeight(brain, "Separation", swarmControl.separationForce); UpdateWeight(brain, "Separation", swarmControl.separationForce);
@ -22,7 +22,7 @@ public class SwarmControl_Editor : Editor {
} }
} }
protected void UpdateWeight(NanoBrainObj brain, string name, float weight) { protected void UpdateWeight(NanoBrain brain, string name, float weight) {
Nucleus root = brain.root; Nucleus root = brain.root;
foreach (Synapse synapse in root.synapses) { foreach (Synapse synapse in root.synapses) {
if (synapse.nucleus.name == name) { if (synapse.nucleus.name == name) {

File diff suppressed because it is too large Load Diff

Binary file not shown.