diff --git a/Cluster.cs b/Cluster.cs index 7c77590..fc8f88f 100644 --- a/Cluster.cs +++ b/Cluster.cs @@ -7,38 +7,31 @@ using static Unity.Mathematics.math; [System.Serializable] public class Cluster : INucleus { // The ScriptableObject asset from which the runtime object has been created - [SerializeField] - public ClusterPrefab prefab; + public ClusterPrefab storedPrefab; + //public ClusterPrefab prefab; - public ClusterPrefab cluster { get; set; } - - [SerializeField] - protected string _name; - public virtual string name { - get => _name; - set => _name = value; + public Cluster() { + } + public Cluster(Cluster parent) { + this.parent = parent; } public Cluster(ClusterPrefab prefab) { - this.prefab = UnityEngine.Object.Instantiate(prefab); + this.storedPrefab = prefab; + //this.prefab = prefab.Clone(); this.name = prefab.name; this.cluster = null; if (this.cluster != null) - this.cluster.nuclei.Add(this); + this.cluster.nuclei.Add(this); } - // Hmm, a cluster instance can never be part of a scriptable object...(Cluster) - public Cluster(ClusterPrefab parent, ClusterPrefab prefab) { - this.prefab = prefab.Clone(); //UnityEngine.Object.Instantiate(prefab); - this.name = prefab.name; + public Cluster(ClusterPrefab parent, ClusterPrefab realPrefab) { + this.storedPrefab = realPrefab; + //this.prefab = realPrefab.Clone(); + this.name = realPrefab.name; this.cluster = parent; if (this.cluster != null) this.cluster.nuclei.Add(this); - - // foreach (IReceptor nucleus in this.prefab.nuclei) { - // IReceptor clone = nucleus.CloneTo(null); - // this.dynamicNuclei.Add(clone); - // } } public virtual IReceptor Clone() { @@ -56,7 +49,112 @@ public class Cluster : INucleus { return clone; } - public INucleus output => prefab.output; + // public IReceptor CloneTo(ClusterPrefab parent) { + // Cluster clone = new(parent, this.prefab); + // return clone; + // } + + public IReceptor ShallowCloneTo(Cluster parent) { + Cluster clone = new(parent); + return clone; + } + + // public IReceptor ShallowCloneTo(ClusterPrefab parent) { + // Cluster clone = new(parent, this.prefab); + // return clone; + // } + + // Deep clone a nucleus with its connections + public virtual Cluster InstantiatePrefab(ClusterPrefab prefab) { + Cluster clone = new Cluster { + nuclei = new() + }; + + IReceptor[] nucleiArray = this.nuclei.ToArray(); + // first clone the nuclei without their connections + foreach (IReceptor nucleus in this.nuclei) + nucleus.ShallowCloneTo(clone); + IReceptor[] clonedNuclei = clone.nuclei.ToArray(); + + // Now clone the connections + for (int nucleusIx = 0; nucleusIx < nucleiArray.Length; nucleusIx++) { + IReceptor receptor = nucleiArray[nucleusIx]; + IReceptor clonedReceptor = clonedNuclei[nucleusIx]; + if (clonedReceptor == null) + continue; + + // Copy the synapses + if (receptor is INucleus nucleus) { + foreach (Synapse synapse in nucleus.synapses) { + if (clonedReceptor is not INucleus clonedNucleus) + continue; + + int ix = GetNucleusIndex(nucleiArray, synapse.nucleus); + if (ix < 0) + continue; + IReceptor clonedSynapseNucleus = clonedNuclei[ix]; + if (clonedSynapseNucleus == null) + continue; + + clonedNucleus.AddSynapse(clonedSynapseNucleus, synapse.weight); + } + } + // Copy the receivers + foreach (INucleus receiver in nucleiArray[nucleusIx].receivers) { + int ix = GetNucleusIndex(nucleiArray, receiver); + if (ix < 0) + continue; + + if (clonedNuclei[ix] is not INucleus clonedReceiver) + continue; + + clonedReceptor.AddReceiver(clonedReceiver); + } + } + + return clone; + } + + private int GetNucleusIndex(IReceptor[] nucleiArray, IReceptor nucleus) { + for (int i = 0; i < nucleiArray.Length; i++) { + if (nucleus == nucleiArray[i]) + return i; + } + return -1; + } + + public ClusterPrefab cluster { get; set; } + public Cluster parent { get; set; } + + [SerializeReference] + public List nuclei = new(); + + [SerializeField] + protected string _name; + public virtual string name { + get => _name; + set => _name = value; + } + + public List _inputs = null; + public virtual List inputs { + get { + if (this._inputs == null) { + this._inputs = new(); + foreach (IReceptor receptor in this.nuclei) { + if (receptor is INucleus nucleus) { + // inputs have no incoming synapses yet. + if (nucleus.synapses.Count == 0) + this._inputs.Add(nucleus); + } + } + } + return this._inputs; + } + } + + //public INucleus output => prefab.output; + public virtual INucleus output => this.nuclei[0] as INucleus; // Not sure if this belongs here... [SerializeReference] @@ -68,27 +166,14 @@ public class Cluster : INucleus { #region Synapses - // class ClusterSynapse : Synapse { - // public IReceptor receptor; - // public ClusterSynapse(IReceptor nucleus, INucleus receptor, float weight = 1.0f) : base(nucleus, weight) { - // this.receptor = receptor; - // } - // } [SerializeField] private List _synapses = new(); public List synapses => _synapses; - public Synapse AddSynapse(IReceptor sendingNucleus) { - Synapse synapse = new(sendingNucleus); //, this.prefab.inputs[0]); + public Synapse AddSynapse(IReceptor sendingNucleus, float weight = 1.0f) { + Synapse synapse = new(sendingNucleus, weight); //, this.prefab.inputs[0]); this._synapses.Add(synapse); return synapse; - // else { - // INucleus receptor = (INucleus)this.prefab.nuclei.Find(nucleus => nucleus is INucleus n && nucleus.name == nucleusName); - // ClusterSynapse synapse = new(sendingNucleus, receptor); - // receptor.AddSynapse(sendingNucleus); - // } - // // Add synapse to which neuron? - // return null; } // Does this even exist already? @@ -117,48 +202,6 @@ public class Cluster : INucleus { receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this); } - // public void AddReceiver(INucleus receivingNucleus) { - // int newLength = this._receivers.Count + 1; - // INucleus[] newReceivers = new INucleus[newLength]; - - // // Copy the existing receivers - // for (int ix = 0; ix < this._receivers.Count; ix++) - // newReceivers[ix] = this._receivers[ix]; - // // Add the new receivers - // newReceivers[this._receivers.Count] = receivingNucleus; - // // Replace the receivers with the new receivers - // this._receivers = new(newReceivers); - - // receivingNucleus.AddSynapse(this); - // } - - // public void RemoveReceiver(INucleus receivingNucleus) { - // Debug.Log("Clusterinstance. remote receiver"); - // int newLength = this._receivers.Count - 1; - // if (newLength < 0) - // // Array was empty, so we cannot remove anything - // return; - - // INucleus[] newReceivers = new INucleus[newLength]; - - // int newIx = 0; - // // Copy all receivers except receivingNucleus - // for (int ix = 0; ix < this._receivers.Count; ix++) { - // if (this._receivers[ix] == receivingNucleus) - // // skip the receiver we want to remote - // continue; - - // if (newIx >= newLength) - // // We want to copy more elements than expected - // // the receivingNucleus is not found - // // and the original array is returned - // return; - // newReceivers[newIx] = this._receivers[ix]; - // newIx++; - // } - // this._receivers = new(newReceivers); - // } - #endregion Receivers #region Runtime @@ -190,10 +233,10 @@ public class Cluster : INucleus { foreach (Synapse synapse in this.synapses) { sum += synapse.weight * synapse.nucleus.outputValue; } - - // This does not work because the prefab nucleus does not have a state - this.prefab.inputs[0].UpdateState(sum); - //this._outputValue = this.output.outputValue; + + //this.prefab.inputs[0].UpdateState(sum); + this.inputs[0].UpdateState(sum); + UpdateResult(this.output.outputValue); } @@ -208,13 +251,14 @@ public class Cluster : INucleus { foreach (INucleus receiver in this.receivers) receiver.UpdateState(); } - + public void UpdateNuclei() { this.stale++; - if (this.stale > 2) + if (this.stale > 2) _outputValue = Vector3.zero; - foreach (IReceptor nucleus in this.prefab.nuclei) + //foreach (IReceptor nucleus in this.prefab.nuclei) + foreach (IReceptor nucleus in this.nuclei) nucleus.UpdateNuclei(); } diff --git a/ClusterPrefab.cs b/ClusterPrefab.cs index 5786820..1beb6b2 100644 --- a/ClusterPrefab.cs +++ b/ClusterPrefab.cs @@ -36,12 +36,67 @@ public class ClusterPrefab : ScriptableObject { } } +/* + // Deep clone a nucleus with its connections public virtual ClusterPrefab Clone() { ClusterPrefab clone = Instantiate(this); + clone.nuclei = new(); + // foreach (IReceptor nucleus in this.nuclei) + // nucleus.CloneTo(clone); + + IReceptor[] nucleiArray = this.nuclei.ToArray(); + // first clone the nuclei without their connections + foreach (IReceptor nucleus in this.nuclei) + nucleus.ShallowCloneTo(clone); + IReceptor[] clonedNuclei = clone.nuclei.ToArray(); + + // Now clone the connections + for (int nucleusIx = 0; nucleusIx < nucleiArray.Length; nucleusIx++) { + IReceptor receptor = nucleiArray[nucleusIx]; + IReceptor clonedReceptor = clonedNuclei[nucleusIx]; + if (clonedReceptor == null) + continue; + + // Copy the synapses + if (receptor is INucleus nucleus) { + foreach (Synapse synapse in nucleus.synapses) { + if (clonedReceptor is not INucleus clonedNucleus) + continue; + + int ix = GetNucleusIndex(nucleiArray, synapse.nucleus); + if (ix < 0) + continue; + IReceptor clonedSynapseNucleus = clonedNuclei[ix]; + if (clonedSynapseNucleus == null) + continue; + + clonedNucleus.AddSynapse(clonedSynapseNucleus, synapse.weight); + } + } + // Copy the receivers + foreach (INucleus receiver in nucleiArray[nucleusIx].receivers) { + int ix = GetNucleusIndex(nucleiArray, receiver); + if (ix < 0) + continue; + + if (clonedNuclei[ix] is not INucleus clonedReceiver) + continue; + + clonedReceptor.AddReceiver(clonedReceiver); + } + } + return clone; } - + private int GetNucleusIndex(IReceptor[] nucleiArray, IReceptor nucleus) { + for (int i = 0; i < nucleiArray.Length; i++) { + if (nucleus == nucleiArray[i]) + return i; + } + return -1; + } +*/ // Call this function to ensure that there is at least one nucleus // This is an invariant and should be ensured before the nucleus is used // because output requires it. diff --git a/INucleus.cs b/INucleus.cs index f7272f3..cd4ba50 100644 --- a/INucleus.cs +++ b/INucleus.cs @@ -10,7 +10,7 @@ public interface INucleus : IReceptor { // Senders public List synapses { get; } - public Synapse AddSynapse(IReceptor sender); + public Synapse AddSynapse(IReceptor sender, float weight = 1.0f); public NucleusArray array { get; set; } @@ -48,6 +48,8 @@ public interface IReceptor { #endregion dynamic + //public IReceptor ShallowCloneTo(ClusterPrefab parent); + public IReceptor ShallowCloneTo(Cluster parent); //public IReceptor CloneTo(ClusterPrefab parent); public IReceptor Clone(); } diff --git a/Neuron.cs b/Neuron.cs index 099ceab..26443d0 100644 --- a/Neuron.cs +++ b/Neuron.cs @@ -88,6 +88,7 @@ public class Neuron : INucleus { #region Runtime state (not serialized) public ClusterPrefab cluster { get; set; } + public Cluster parent { get; set; } #region Activation @@ -159,12 +160,19 @@ public class Neuron : INucleus { this.stale++; // this._isSleeping = this.stale > 2; // if (isSleeping) - if (this.stale > 2) + if (this.stale > 2) _outputValue = Vector3.zero; } #endregion Runtime state + public Neuron(Cluster parent, string name) { + this.parent = parent; + this.name = name; + if (this.cluster != null) { + this.cluster.nuclei.Add(this); + } + } public Neuron(ClusterPrefab parent, string name) { this.cluster = parent; this.name = name; @@ -179,6 +187,28 @@ public class Neuron : INucleus { // this._name = name; // } + // this clone the nucleus without the synapses and receivers + public virtual IReceptor ShallowCloneTo(Cluster newParent) { + Neuron clone = new(newParent, this.name) { + array = this.array, + curve = this.curve, + curvePreset = this.curvePreset, + curveMax = this.curveMax, + average = this.average + }; + return clone; + } + public virtual IReceptor ShallowCloneTo(ClusterPrefab newParent) { + Neuron clone = new(newParent, this.name) { + array = this.array, + curve = this.curve, + curvePreset = this.curvePreset, + curveMax = this.curveMax, + average = this.average + }; + return clone; + } + public virtual IReceptor CloneTo(ClusterPrefab parent) { Neuron clone = new(parent, this.name) { array = this.array, @@ -187,8 +217,6 @@ public class Neuron : INucleus { curveMax = this.curveMax, average = this.average }; - // if (clone.cluster != null) - // clone.cluster.nuclei.Add(clone); foreach (Synapse synapse in this.synapses) { Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus); @@ -254,8 +282,8 @@ public class Neuron : INucleus { } } - public Synapse AddSynapse(IReceptor sendingNucleus) { - Synapse synapse = new(sendingNucleus); + public Synapse AddSynapse(IReceptor sendingNucleus, float weight = 1.0f) { + Synapse synapse = new(sendingNucleus, weight); this.synapses.Add(synapse); return synapse; } diff --git a/Receptor.cs b/Receptor.cs index 74053dd..8d9c81e 100644 --- a/Receptor.cs +++ b/Receptor.cs @@ -1,11 +1,14 @@ +using System; using System.Collections.Generic; using UnityEngine; using Unity.Mathematics; using static Unity.Mathematics.math; +[Serializable] public class Receptor : IReceptor { - private Cluster cluster; + private ClusterPrefab cluster; + private Cluster parent; [SerializeField] protected string _name; @@ -14,16 +17,21 @@ public class Receptor : IReceptor { set => _name = value; } - public Receptor(Cluster cluster) { + public Receptor(Cluster parent) { + this.parent = parent; + if (parent != null) + parent.nuclei.Add(this); + } + public Receptor(ClusterPrefab cluster) { this.cluster = cluster; if (cluster != null) - cluster.prefab.nuclei.Add(this); + cluster.nuclei.Add(this); } - public Receptor(Cluster cluster, INucleus nucleus) { + public Receptor(ClusterPrefab cluster, INucleus nucleus) { this.cluster = cluster; if (cluster != null) - cluster.prefab.nuclei.Add(this); + cluster.nuclei.Add(this); this.AddReceiver(nucleus); } @@ -31,8 +39,10 @@ public class Receptor : IReceptor { if (cluster == null) return null; + //Receptor receptor = new(cluster.prefab); Receptor receptor = new(cluster); - foreach (INucleus nucleus in cluster.prefab.inputs) { + //foreach (INucleus nucleus in cluster.prefab.inputs) { + foreach (INucleus nucleus in cluster.inputs) { if (nucleus != null && nucleus.name == nucleusName) { // Receptor receptor = new(cluster, nucleus); // return receptor; @@ -45,7 +55,16 @@ public class Receptor : IReceptor { return receptor; } - public virtual IReceptor CloneTo(Cluster parent) { + public virtual IReceptor ShallowCloneTo(Cluster parent) { + Receptor clone = new(parent); + return clone; + } + public virtual IReceptor ShallowCloneTo(ClusterPrefab parent) { + Receptor clone = new(parent); + return clone; + } + + public virtual IReceptor CloneTo(ClusterPrefab parent) { Receptor clone = new(parent); foreach (INucleus receiver in this.receivers) { diff --git a/Velocity.asset b/Velocity.asset index 1528d43..aad36ea 100644 --- a/Velocity.asset +++ b/Velocity.asset @@ -13,18 +13,18 @@ MonoBehaviour: m_Name: Velocity m_EditorClassIdentifier: Assembly-CSharp::ClusterPrefab nuclei: - - rid: 2243601420268863545 + - rid: 2243601425629446539 references: version: 2 RefIds: - - rid: 2243601420268863545 + - rid: 2243601425629446539 type: {class: Neuron, ns: , asm: Assembly-CSharp} data: _name: Output _synapses: [] _receivers: [] _array: - rid: 2243601420268863546 + rid: 2243601425629446540 _curvePreset: 0 curve: serializedVersion: 2 @@ -52,9 +52,9 @@ MonoBehaviour: m_RotationOrder: 4 curveMax: 1 average: 0 - - rid: 2243601420268863546 + - rid: 2243601425629446540 type: {class: NucleusArray, ns: , asm: Assembly-CSharp} data: _nuclei: - - rid: 2243601420268863545 + - rid: 2243601425629446539 name: Output diff --git a/VisualEditor/Editor/ClusterInspector.cs b/VisualEditor/Editor/ClusterInspector.cs index 413b87f..6e2022a 100644 --- a/VisualEditor/Editor/ClusterInspector.cs +++ b/VisualEditor/Editor/ClusterInspector.cs @@ -620,19 +620,16 @@ public class ClusterInspector : Editor { ClusterPickerWindow.ShowPicker(brain => OnClusterPicked(nucleus, brain), "Select Cluster"); } - private void OnClusterPicked(INucleus nucleus, ClusterPrefab subCluster) { - Cluster subclusterInstance = new(this.cluster, subCluster); - //this.cluster.AddSubCluster(subclusterInstance); - //this.cluster.nuclei.Add(subclusterInstance); + private void OnClusterPicked(INucleus nucleus, ClusterPrefab prefab) { + Cluster subclusterInstance = new(this.cluster, prefab); subclusterInstance.AddReceiver(nucleus); } private void EditCluster(Cluster subCluster) { - //var currentActiveObject = Selection.activeObject; - Selection.activeObject = subCluster.prefab; - EditorGUIUtility.PingObject(subCluster.prefab); - var editor = Editor.CreateEditor(subCluster.prefab); - //Selection.activeObject = currentActiveObject; + // May be used with storedPrefab... + Selection.activeObject = subCluster.storedPrefab; + EditorGUIUtility.PingObject(subCluster.storedPrefab); + var editor = Editor.CreateEditor(subCluster.storedPrefab); } // Connect to another nucleus in the same cluster diff --git a/VisualEditor/Editor/NanoBrainComponent_Editor.cs b/VisualEditor/Editor/NanoBrainComponent_Editor.cs index 6e60376..8b15fe6 100644 --- a/VisualEditor/Editor/NanoBrainComponent_Editor.cs +++ b/VisualEditor/Editor/NanoBrainComponent_Editor.cs @@ -22,8 +22,8 @@ public class NanoBrainComponent_Editor : Editor { } public override VisualElement CreateInspectorGUI() { - //NanoBrainComponent component = target as NanoBrainComponent; - ClusterPrefab brain = Application.isPlaying ? component.brain.prefab : component.defaultBrain; + //ClusterPrefab brain = Application.isPlaying ? component.brain.prefab : component.defaultBrain; + Cluster brain = component.brain; if (Application.isPlaying == false) serializedObject.Update(); @@ -79,7 +79,7 @@ public class NanoBrainComponent_Editor : Editor { }); if (brain != null && board != null) - board.SetGraph(component.gameObject, brain, brain.output, inspectorContainer); + board.SetGraph(component.gameObject, brain.storedPrefab, brain.output, inspectorContainer); // else // Debug.LogWarning(" No brain!"); diff --git a/VisualEditor/NanoBrainComponent.cs b/VisualEditor/NanoBrainComponent.cs index d5b39c9..5ed427f 100644 --- a/VisualEditor/NanoBrainComponent.cs +++ b/VisualEditor/NanoBrainComponent.cs @@ -1,6 +1,7 @@ using UnityEngine; public class NanoBrainComponent : MonoBehaviour { + [SerializeField] public ClusterPrefab defaultBrain; private Cluster brainInstance;