using System; using System.Collections.Generic; using UnityEngine; using Unity.Mathematics; using static Unity.Mathematics.math; using System.Linq; [Serializable] public class ClusterReceptor : Cluster, IReceptor { public ClusterReceptor(ClusterPrefab prefab, Cluster parent, string name) : base(prefab, parent) { this.name = name; this.array = new NucleusArray(this); if (this.name.IndexOf(":") < 0) this.name += ": 0"; } public ClusterReceptor(ClusterPrefab prefab, ClusterPrefab parent, string name) : base(prefab, parent) { this.name = name; this.array = new NucleusArray(this); } public string GetName() { return this.name; } public override Nucleus ShallowCloneTo(Cluster parent) { ClusterReceptor clone = new(this.prefab, parent, this.name) { clusterPrefab = this.clusterPrefab, //array = this.array }; //CloneFields(clone); // This cloned the prefab with the clusternuclei, // but did not clone the receivers outside the cluster //RestoreExternalReceivers(clone, this.clusterPrefab, parent); return clone; } public override Nucleus Clone(ClusterPrefab parent) { ClusterReceptor clone = new(prefab, parent, this.name) { array = this._array }; //CloneFields(clone); foreach (Synapse synapse in this.synapses) { Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus); clonedSynapse.weight = synapse.weight; } // foreach (Nucleus receiver in this.receivers) { // clone.AddReceiver(receiver); // } this._outputs = null; // Make sure the output are regenerated foreach (Neuron output in this.outputs) { int ix = GetNucleusIndex(this.clusterNuclei, output); if (ix < 0 || clone.clusterNuclei[ix] is not Neuron clonedOutput) continue; foreach (Nucleus receiver in output.receivers) clonedOutput.AddReceiver(receiver); } return clone; } public override List CollectReceivers() { List receivers = new(); foreach (Nucleus element in this.nucleiArray) { if (element is not Cluster clusterElement) continue; foreach (Nucleus outputNucleus in clusterElement.clusterNuclei) { if (outputNucleus is not Neuron output) continue; // this should be clusterElement.outputs, // but outputs is not updated when correctly and may contain old data... foreach (Nucleus receiver in output.receivers) { // Only add receivers outside clusterElement cluster if (receiver.clusterPrefab != clusterElement.prefab && receivers.Contains(receiver) == false) receivers.Add(receiver); } } } return receivers; } [SerializeReference] private NucleusArray _array; public NucleusArray array { //get { return _array; } set { _array = value; } } //[SerializeReference] //private Nucleus[] _nucleusArray; public Nucleus[] nucleiArray { get { return _array.nuclei; } set { _array.nuclei = value; } } //public ClusterReceptor[] nucleiArray; public void AddReceptorElement(ClusterPrefab prefab) { IReceptorHelpers.AddReceptorElement(this, prefab); } public void RemoveReceptorElement() { IReceptorHelpers.RemoveReceptorElement(this); } public void AddArrayReceiver(Nucleus receiverToAdd, float weight = 1) { IReceptorHelpers.AddArrayReceiver(this, receiverToAdd, weight); } public override void UpdateStateIsolated() { // Clusters don't do anything, // The nuclei in them do the work // and should be called directly, not from the cluster // float3 sum = this.bias; // foreach (Nucleus nucleus in this.sortedNuclei) // nucleus.UpdateStateIsolated(); // this.outputValue = this.defaultOutput.outputValue; // this.stale = 0; // UpdateNuclei(); } public override void UpdateNuclei() { // this.stale++; // if (this.stale > staleValueForSleep && lengthsq(this.bias) > 0) { // this.bias = new float3(0, 0, 0); // this.parent.UpdateFromNucleus(this); // } foreach (Nucleus nucleus in this.clusterNuclei) nucleus.UpdateNuclei(); } public override void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null) { //this._array ??= new NucleusArray(this.parent); //this._array.ProcessStimulus(thingId, inputValue, thingName); Debug.LogError("Process Stimulus was called on clusterreceptor without a neuron specified"); } private readonly Dictionary thingReceivers = new(); public virtual void ProcessStimulus(Neuron input, Vector3 inputValue, int thingId = 0, string thingName = null) { CleanupReceivers(); Debug.Log($"{input.name} is {inputValue}, 1/{inputValue} = {1 / inputValue.x}, {1/inputValue.y}, {1/inputValue.z}"); //inputValue = input.Activator(inputValue); if (!thingReceivers.TryGetValue(thingId, out ClusterReceptor selectedReceiver)) selectedReceiver = FindReceiver2(thingId, inputValue); if (selectedReceiver == null) return; if (thingName != null) { string baseName = selectedReceiver.name; int colonPos = selectedReceiver.name.IndexOf(":"); if (colonPos > 0) baseName = selectedReceiver.name[..colonPos]; selectedReceiver.name = baseName + ": " + thingName; } int inputIx = GetNucleusIndex(this.clusterNuclei, input); if (inputIx < 0) return; if (selectedReceiver.clusterNuclei[inputIx] is Neuron selectedNeuron) selectedNeuron.ProcessStimulusDirect(inputValue); Debug.Log($"cluster output = {selectedReceiver.defaultOutput.outputValue}"); } // private ClusterReceptor FindReceiver(int thingId, float3 inputValue) { // // No existing nucleus for this thing // float inputMagnitude = length(inputValue); // ClusterReceptor selectedReceiver = null; // float selectedMagnitude = 0; // foreach (ClusterReceptor receiver in nucleiArray.Cast()) { // if (thingReceivers.ContainsValue(receiver) == false) { // // We found an unusued receiver // thingReceivers.Add(thingId, receiver); // return receiver; // } // else if (receiver.isSleeping) { // // A sleeping receiver is not active and can therefore always be used // thingReceivers.Add(thingId, receiver); // return receiver; // } // else if (selectedReceiver == null) { // // If we haven't found a receiver yet, just start by taking the first // selectedReceiver = receiver; // selectedMagnitude = length(selectedReceiver.outputValue); // } // // Look for the receiver with the lowest magnitude // else { // float magnitude = length(receiver.outputValue); // if (magnitude < inputMagnitude && length(receiver.outputValue) < selectedMagnitude) { // selectedReceiver = receiver; // selectedMagnitude = length(selectedReceiver.outputValue); // } // } // } // if (selectedReceiver != null) { // // Replace the receiver // // Find the thingId current associated with the receiver // int keyToRemove = thingReceivers.FirstOrDefault(r => r.Value.Equals(selectedReceiver)).Key; // if (keyToRemove != 0 || thingReceivers.ContainsKey(keyToRemove)) // thingReceivers.Remove(keyToRemove); // // And add the new association // thingReceivers.Add(thingId, selectedReceiver); // } // return selectedReceiver; // } private ClusterReceptor FindReceiver2(int thingId, float3 inputValue) { // No existing nucleus for this thing ClusterReceptor selectedReceiver = null; float selectedMagnitude = 0; foreach (ClusterReceptor receiver in this.nucleiArray.Cast()) { if (thingReceivers.ContainsValue(receiver) == false) { // We found an unusued receiver thingReceivers.Add(thingId, receiver); return receiver; } else if (receiver.defaultOutput.isSleeping) { // A sleeping receiver is not active and can therefore always be used thingReceivers.Add(thingId, receiver); return receiver; } else if (selectedReceiver == null) { // If we haven't found a receiver yet, just start by taking the first selectedReceiver = receiver; selectedMagnitude = length(selectedReceiver.defaultOutput.outputValue); } // Look for the receiver with the lowest output magnitude else { float magnitude = length(receiver.defaultOutput.outputValue); if (length(receiver.defaultOutput.outputValue) < selectedMagnitude) { selectedReceiver = receiver; selectedMagnitude = length(selectedReceiver.defaultOutput.outputValue); } } } if (selectedReceiver != null) { // Replace the receiver // Find the thingId current associated with the receiver int keyToRemove = thingReceivers.FirstOrDefault(r => r.Value.Equals(selectedReceiver)).Key; if (keyToRemove != 0 || thingReceivers.ContainsKey(keyToRemove)) thingReceivers.Remove(keyToRemove); // And add the new association thingReceivers.Add(thingId, selectedReceiver); } return selectedReceiver; } private void CleanupReceivers() { // Remove a thing-receiver connection when the nucleus is inactive List receiversToRemove = new(); foreach (KeyValuePair item in thingReceivers) { if (item.Value != null && item.Value.defaultOutput.isSleeping) receiversToRemove.Add(item.Key); } foreach (int thingId in receiversToRemove) { Nucleus selectedReceiver = thingReceivers[thingId]; thingReceivers.Remove(thingId); int colonPos = selectedReceiver.name.IndexOf(":"); if (colonPos > 0) selectedReceiver.name = selectedReceiver.name[..colonPos]; } } }