diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj index 1f5f66b..cc796da 100644 --- a/Assembly-CSharp.csproj +++ b/Assembly-CSharp.csproj @@ -76,6 +76,7 @@ + diff --git a/Assets/NanoBrain/MemoryCell.cs b/Assets/NanoBrain/MemoryCell.cs new file mode 100644 index 0000000..5e32009 --- /dev/null +++ b/Assets/NanoBrain/MemoryCell.cs @@ -0,0 +1,65 @@ +using System; +using UnityEngine; +using Unity.Mathematics; +using static Unity.Mathematics.math; + +[Serializable] +public class MemoryCell : Neuron { + + public MemoryCell(Cluster cluster, string name) : base(cluster, name) {} + + #region Parameters + + // Returns the memorized value weighted by time + // return lastValue * (current time - last time) + [SerializeField] + public bool deltaValue = false; + + #endregion Parameters + + #region State + + private float3 _memorizedValue; + private float _memorizedTime; + + public override void UpdateState() { + // A memorycell does not have an activation function + float3 result = new(0, 0, 0); + int n = 0; + + //Applying the weight factgors + foreach (Synapse synapse in this.synapses) { + if (synapse.nucleus == this) { + float deltaTime = Time.time - this.lastTime; + synapse.weight = deltaTime; + } + result += synapse.weight * synapse.nucleus.outputValue; + if (lengthsq(synapse.nucleus.outputValue) != 0) + n++; + } + + if (this.average) + result /= n; + + UpdateResult(result); + } + + public override void UpdateResult(Vector3 result) { + // output value is the previous value + if (this.deltaValue) { + float deltaTime = Time.time - this._memorizedTime; + this._outputValue = this._memorizedValue * deltaTime; + } + else + this._outputValue = this._memorizedValue; + + // Store the result for the next time + this._memorizedValue = result; + this._memorizedTime = Time.time; + + foreach (INucleus receiver in this.receivers) + receiver.UpdateState(); + } + + #endregion State +} diff --git a/Assets/NanoBrain/MemoryCell.cs.meta b/Assets/NanoBrain/MemoryCell.cs.meta new file mode 100644 index 0000000..ef74aba --- /dev/null +++ b/Assets/NanoBrain/MemoryCell.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 29633aa3fe5cd9dcc8d886051f45d4d8 \ No newline at end of file diff --git a/Assets/NanoBrain/Neuron.cs b/Assets/NanoBrain/Neuron.cs index 3ccaaa6..ff14d0c 100644 --- a/Assets/NanoBrain/Neuron.cs +++ b/Assets/NanoBrain/Neuron.cs @@ -54,8 +54,13 @@ public class Neuron : INucleus { } public AnimationCurve curve; public float curveMax = 1.0f; + + #region Parameters + public bool average = false; + #endregion Parameters + public AnimationCurve GenerateCurve() { switch (this.curvePreset) { case CurvePresets.Linear: @@ -132,8 +137,8 @@ public class Neuron : INucleus { #endregion Activation - private float3 _outputValue; - public float3 outputValue { + protected float3 _outputValue; + public virtual float3 outputValue { get { return _outputValue; } set { this.stale = 0; @@ -147,6 +152,7 @@ public class Neuron : INucleus { private bool _isSleeping = false; public bool isSleeping => _isSleeping; + public float lastTime { get; private set; } public void UpdateNuclei() { this.stale++; @@ -238,11 +244,16 @@ public class Neuron : INucleus { //Applying the weight factgors foreach (Synapse synapse in this.synapses) { + if (synapse.nucleus == this) { + float deltaTime = Time.time - this.lastTime; + synapse.weight = deltaTime; + } sum += synapse.weight * synapse.nucleus.outputValue; + // Perhaps synapses should be removed when the output value goes to 0.... if (lengthsq(synapse.nucleus.outputValue) != 0) n++; } - if (average) + if (this.average) sum /= n; // Activation function @@ -267,7 +278,8 @@ public class Neuron : INucleus { } UpdateResult(result); } - public void UpdateResult(Vector3 result) { + + public virtual void UpdateResult(Vector3 result) { // float d = Vector3.Distance(result, this.outputValue); // if (d < 0.5f) { // //Debug.Log($"insignificant update: {d}"); @@ -275,6 +287,7 @@ public class Neuron : INucleus { // } this.outputValue = result; + this.lastTime = Time.time; foreach (INucleus receiver in this.receivers) receiver.UpdateState(); diff --git a/Assets/NanoBrain/Velocity.asset b/Assets/NanoBrain/Velocity.asset new file mode 100644 index 0000000..9615d6d --- /dev/null +++ b/Assets/NanoBrain/Velocity.asset @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 60a957541c24c57e78018c202ebb1d9b, type: 3} + m_Name: Velocity + m_EditorClassIdentifier: Assembly-CSharp::Cluster + asset: {fileID: 0} + nuclei: + - rid: 2243601362379866169 + references: + version: 2 + RefIds: + - rid: 2243601362379866169 + type: {class: Neuron, ns: , asm: Assembly-CSharp} + data: + _name: Output + _synapses: [] + _receivers: [] + _array: + rid: 2243601362379866170 + _curvePreset: 0 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1000 + value: 1000 + inSlope: 1 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + curveMax: 1 + average: 0 + - rid: 2243601362379866170 + type: {class: NucleusArray, ns: , asm: Assembly-CSharp} + data: + _nuclei: + - rid: 2243601362379866169 + name: Output diff --git a/Assets/NanoBrain/Velocity.asset.meta b/Assets/NanoBrain/Velocity.asset.meta new file mode 100644 index 0000000..07ecb98 --- /dev/null +++ b/Assets/NanoBrain/Velocity.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd622ac7ed09e70ea8edac595047ac82 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs b/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs index 86a09fd..cd4d6c7 100644 --- a/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs +++ b/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs @@ -488,14 +488,20 @@ public class ClusterInspector : Editor { this.currentNucleus.name = EditorGUILayout.TextField(this.currentNucleus.name); if (this.currentNucleus is Neuron neuroid) { - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150)); - if (neuroid.curveMax > 0) - EditorGUILayout.CurveField(neuroid.curve, Color.cyan, new Rect(0, 0, 1, neuroid.curveMax)); - else - EditorGUILayout.CurveField(neuroid.curve, Color.cyan, new Rect(0, neuroid.curveMax, 1, -neuroid.curveMax)); - neuroid.curvePreset = (Neuron.CurvePresets)EditorGUILayout.EnumPopup(neuroid.curvePreset, GUILayout.Width(100)); - EditorGUILayout.EndHorizontal(); + if (this.currentNucleus is MemoryCell memory) { + // should use serializedProperty + memory.deltaValue = EditorGUILayout.Toggle("DeltaValue", memory.deltaValue); + } + else { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150)); + if (neuroid.curveMax > 0) + EditorGUILayout.CurveField(neuroid.curve, Color.cyan, new Rect(0, 0, 1, neuroid.curveMax)); + else + EditorGUILayout.CurveField(neuroid.curve, Color.cyan, new Rect(0, neuroid.curveMax, 1, -neuroid.curveMax)); + neuroid.curvePreset = (Neuron.CurvePresets)EditorGUILayout.EnumPopup(neuroid.curvePreset, GUILayout.Width(100)); + EditorGUILayout.EndHorizontal(); + } if (neuroid.array == null || neuroid.array.nuclei == null || neuroid.array.nuclei.Count() == 0) neuroid.array = new NucleusArray(neuroid); @@ -544,6 +550,8 @@ public class ClusterInspector : Editor { ConnectNucleus(this.cluster, this.currentNucleus); if (GUILayout.Button("Add Input Neuron")) AddInputNeuron(this.currentNucleus); + if (GUILayout.Button("Add Input MemoryCell")) + AddInputMemoryCell(this.currentNucleus); if (GUILayout.Button("Add Input Cluster")) AddCluster(this.currentNucleus); @@ -598,6 +606,13 @@ public class ClusterInspector : Editor { BuildLayers(); } + protected virtual void AddInputMemoryCell(INucleus nucleus) { + MemoryCell newMemory = new(this.cluster.cluster, "New memory cell"); + newMemory.AddReceiver(nucleus); + this.currentNucleus = newMemory; + BuildLayers(); + } + protected virtual void AddCluster(INucleus nucleus) { BrainPickerWindow.ShowPicker(brain => OnClusterPicked(nucleus, brain), "Select Cluster"); }