From c011e0465062c134dbfeeda998d2e8060c2506f2 Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Tue, 10 Feb 2026 15:02:24 +0100 Subject: [PATCH] Add static memory option --- Editor/ClusterInspector.cs | 105 ++++++++++++++----------------------- MemoryCell.cs | 49 +++++++++++------ Nucleus.cs | 8 +-- 3 files changed, 74 insertions(+), 88 deletions(-) diff --git a/Editor/ClusterInspector.cs b/Editor/ClusterInspector.cs index c675e7b..e90ba23 100644 --- a/Editor/ClusterInspector.cs +++ b/Editor/ClusterInspector.cs @@ -396,7 +396,7 @@ public class ClusterInspector : Editor { } private void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue, float size, Color color) { - if (nucleus is MemoryCell memory) { + if (nucleus is MemoryCell) { Handles.color = Color.white; Handles.DrawWireDisc(position + Vector3.right * 10, Vector3.forward, size); } @@ -413,17 +413,17 @@ public class ClusterInspector : Editor { normal = { textColor = Color.white }, fontStyle = FontStyle.Bold, }; - if (nucleus is Nucleus neuron) { - if (neuron.array == null || neuron.array.nuclei == null || neuron.array.nuclei.Count() == 0) - neuron.array = new NucleusArray(neuron); + //if (nucleus is Nucleus neuron) { + if (nucleus.array == null || nucleus.array.nuclei == null || nucleus.array.nuclei.Count() == 0) + nucleus.array = new NucleusArray(nucleus); - if ((!expandArray || neuron.array.nuclei.First() != this.currentNucleus) && neuron.array.nuclei.Count() > 1) { - Handles.Label(labelPosition, neuron.array.nuclei.Count().ToString(), style); + if ((!expandArray || nucleus.array.nuclei.First() != this.currentNucleus) && nucleus.array.nuclei.Count() > 1) { + Handles.Label(labelPosition, nucleus.array.nuclei.Count().ToString(), style); } - if (expandArray && neuron.array.nuclei.First() == this.currentNucleus) { + if (expandArray && nucleus.array.nuclei.First() == this.currentNucleus) { int arrayIx = 0; - foreach (Nucleus n in neuron.array.nuclei) { - if (n == neuron) + foreach (Nucleus n in nucleus.array.nuclei) { + if (n == nucleus) break; arrayIx++; } @@ -441,17 +441,18 @@ public class ClusterInspector : Editor { Handles.Label(labelPos, nucleus.name, style); } - if (nucleus is Cluster cluster) { + if (nucleus is Cluster) { Handles.color = Color.white; Handles.DrawWireDisc(position, Vector3.forward, size + 10); } - } - else { - style.alignment = TextAnchor.UpperCenter; - Vector3 labelPos = position - Vector3.down * (size + 10); // below disc along up axis - Handles.Label(labelPos, nucleus.name, style); - } + // } + // else { + // style.alignment = TextAnchor.UpperCenter; + // Vector3 labelPos = position - Vector3.down * (size + 10); // below disc along up axis + // Handles.Label(labelPos, nucleus.name, style); + // } + // Tooltip Rect neuronRect = new(position.x - size, position.y - size, size * 2, size * 2); int id = GUIUtility.GetControlID(FocusType.Passive); Event e = Event.current; @@ -533,31 +534,6 @@ public class ClusterInspector : Editor { outputsField.choices = this.prefab.outputs.Select(output => output.name).ToList(); //outputsField.value = newName; } - //if (this.currentNucleus is Neuron neuron) { - // if (this.currentNucleus is MemoryCell memory) { - // } - // else { - // EditorGUILayout.BeginHorizontal(); - // EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150)); - // if (neuron.curveMax > 0) - // EditorGUILayout.CurveField(neuron.curve, Color.cyan, new Rect(0, 0, 1, neuron.curveMax)); - // else - // EditorGUILayout.CurveField(neuron.curve, Color.cyan, new Rect(0, neuron.curveMax, 1, -neuron.curveMax)); - // neuron.curvePreset = (Neuron.CurvePresets)EditorGUILayout.EnumPopup(neuron.curvePreset, GUILayout.Width(100)); - // EditorGUILayout.EndHorizontal(); - // } - - // if (neuron.array == null || neuron.array.nuclei == null || neuron.array.nuclei.Count() == 0) - // neuron.array = new NucleusArray(neuron); - // EditorGUILayout.BeginHorizontal(); - // EditorGUILayout.IntField("Array size", neuron.array.nuclei.Count()); - // if (GUILayout.Button("Add")) - // neuron.array.AddNucleus(this.prefab); - // if (GUILayout.Button("Del")) - // neuron.array.RemoveNucleus(); - // EditorGUILayout.EndHorizontal(); - //} - if (Application.isPlaying) { GUIContent nameLabel = new("Output", this.currentNucleus.outputValue.ToString()); @@ -566,6 +542,12 @@ public class ClusterInspector : Editor { else EditorGUILayout.LabelField(" "); + if (this.currentNucleus is MemoryCell memory) { + memory.staticMemory = EditorGUILayout.Toggle("Static Memory", memory.staticMemory); + } + + // Synapses + showSynapses = EditorGUILayout.BeginFoldoutHeaderGroup(showSynapses, "Synapses"); if (showSynapses) { ConnectNucleus(this.prefab, this.currentNucleus); @@ -616,7 +598,7 @@ public class ClusterInspector : Editor { if (showActivation) { if (this.currentNucleus is Neuron neuron) { - if (this.currentNucleus is not MemoryCell memory) { + if (this.currentNucleus is not MemoryCell) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150)); if (neuron.curveMax > 0) @@ -650,13 +632,6 @@ public class ClusterInspector : Editor { EditCluster(subCluster); } - // if (this.gameObject != null) { - // Vector3 worldVector = this.gameObject.transform.TransformVector(this.currentNucleus.outputValue); - // //Debug.DrawRay(this.gameObject.transform.position, worldVector, Color.yellow); - // Handles.color = Color.yellow; - // Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector); - // } - EditorGUILayout.Space(); breakOnWake = EditorGUILayout.Toggle("Break on wake", breakOnWake); if (breakOnWake) { @@ -700,22 +675,22 @@ public class ClusterInspector : Editor { } } - protected virtual void AddInput(int selectedInputType, Nucleus nucleus) { - switch (selectedInputType) { - case 0: // Neuron - AddNeuronInput(nucleus); - break; - case 1: // MemoryCell - AddMemoryCellInput(nucleus); - break; - case 2: // Selector - AddSelectorInput(nucleus); - break; - case 3: // Cluster - AddClusterInput(nucleus); - break; - } - } + // protected virtual void AddInput(int selectedInputType, Nucleus nucleus) { + // switch (selectedInputType) { + // case 0: // Neuron + // AddNeuronInput(nucleus); + // break; + // case 1: // MemoryCell + // AddMemoryCellInput(nucleus); + // break; + // case 2: // Selector + // AddSelectorInput(nucleus); + // break; + // case 3: // Cluster + // AddClusterInput(nucleus); + // break; + // } + // } protected virtual void AddNeuronInput(Nucleus nucleus) { //Neuron newNeuroid = new(this.cluster, "New neuron"); diff --git a/MemoryCell.cs b/MemoryCell.cs index 796c6d8..5eb13d6 100644 --- a/MemoryCell.cs +++ b/MemoryCell.cs @@ -9,27 +9,30 @@ public class MemoryCell : Neuron { public MemoryCell(ClusterPrefab cluster, string name) : base(cluster, name) { } public MemoryCell(Cluster parent, string name) : base(parent, name) { } + public bool staticMemory = false; + public override bool isSleeping { + get { + if (staticMemory) + return false; + + return base.isSleeping; + } + } + public override Nucleus ShallowCloneTo(Cluster newParent) { - MemoryCell clone = new(newParent, this.name) { - array = this.array, - curve = this.curve, - curvePreset = this.curvePreset, - curveMax = this.curveMax, - average = this.average - }; + MemoryCell clone = new(newParent, this.name); + CloneFields(clone); + clone.staticMemory = this.staticMemory; return clone; } #region State - private float3 _memorizedValue; - private float _memorizedTime; + private bool initialized = false; - // public override void UpdateStateIsolated() { - // float3 bias = new(0, 0, 0); - // UpdateStateIsolated(bias); - // } - public override void UpdateStateIsolated() { //float3 bias) { + private float3 _memorizedValue; + + public override void UpdateStateIsolated() { // A memorycell does not have an activation function Vector3 result = this.bias; int n = 0; @@ -44,11 +47,25 @@ public class MemoryCell : Neuron { if (this.average) result /= n; - this.outputValue = this._memorizedValue; + if (initialized) + // Output the previous, memorized value + this.outputValue = this._memorizedValue; + else { + // The first time, the result is directly set in output + this.outputValue = result; + this.initialized = true; + } // Store the result for the next time this._memorizedValue = result; - this._memorizedTime = Time.time; + } + + public override void UpdateNuclei() { + if (staticMemory) + // Static memory does not get stale or go to sleep + return; + + base.UpdateNuclei(); } #endregion State diff --git a/Nucleus.cs b/Nucleus.cs index baef87e..01d2a80 100644 --- a/Nucleus.cs +++ b/Nucleus.cs @@ -27,7 +27,7 @@ public abstract class Nucleus { public bool isFiring => length(_outputValue) > 0.5f; public Action WhenFiring; - public bool isSleeping => lengthsq(this.outputValue) == 0; + public virtual bool isSleeping => lengthsq(this.outputValue) == 0; [NonSerialized] public int stale = 1000; public readonly int staleValueForSleep = 20; @@ -99,12 +99,6 @@ public abstract class Nucleus { #region Update public abstract void UpdateStateIsolated(); - // UpdateStateIsolated(new float3(0, 0, 0)); - // } - - //public abstract void UpdateStateIsolated(float3 bias); - // public virtual void UpdateStateIsolated(float3 bias) { - // } public virtual void UpdateNuclei() { this.stale++;