using System; using UnityEngine; using Unity.Mathematics; namespace NanoBrain { /// /// A Synapse connects the ouput of a Neuron to another Neuron /// [Serializable] public class Synapse { /// /// The neuron from which input is received /// [SerializeReference] [HideInInspector] public Neuron neuron; /// /// The weight value to apply to the Neuron input /// public float weight; /// /// Indicator whether the weight can be trained /// public bool trainable = false; /// /// Create a new Synapse /// /// The neuron from which input is received /// The weight value to apply to the Neuron input public Synapse(Neuron nucleus, float weight = 1.0f) { this.neuron = nucleus; this.weight = weight; } public virtual void BackPropagation(Neuron receiver, float derivative, float learningRate) { switch (receiver.activator) { case Neuron.ActivationType.Linear: derivative *= 1; break; case Neuron.ActivationType.Power: // untested derivative *= 2 * math.length(this.neuron.combination); break; case Neuron.ActivationType.Reciprocal: // untested derivative *= -1 / Mathf.Pow(math.length(this.neuron.combination), 2); break; default: Debug.Log("other activator"); break; } this.neuron.BackPropagation1D(derivative * this.weight, learningRate); derivative *= math.length(this.neuron.activation); if (this.trainable) { float deltaWeight = learningRate * derivative; this.weight += deltaWeight; } } public virtual void BackPropagation3D(Neuron receiver, Vector3 derivative, float learningRate) { // As the weight cannot change the direction of the derivative // we can use the simpler, 1D backpropagation here // But we still need to determine the sign of the derivative if (Synapse.AreOpposed(derivative, this.neuron.activation)) BackPropagation(receiver, -derivative.magnitude, learningRate); else BackPropagation(receiver, derivative.magnitude, learningRate); // switch (receiver.activator) { // case Neuron.ActivationType.Linear: // derivative *= 1; // break; // case Neuron.ActivationType.Power: // // untested // derivative *= 2 * math.length(this.neuron.combination); // break; // case Neuron.ActivationType.Reciprocal: // // untested // derivative *= -1 / Mathf.Pow(math.length(this.neuron.combination), 2); // break; // default: // Debug.Log("other activator"); // break; // } // this.neuron.BackPropagation3D(derivative * this.weight, learningRate); // derivative *= math.length(this.neuron.activation); // if (this.trainable) { // float deltaWeight = learningRate * derivative.magnitude; // // Compared to the 1D solution, this does not decrease the weight because magnitude is always positive // // derivative.direction and derivative.sign are different.... // if (AreOpposed(derivative, this.neuron.activation)) // this.weight -= deltaWeight; // else // this.weight += deltaWeight; // } } public static bool AreOpposed(Vector3 a, Vector3 b) { // Check if the angle between the vectors is > 90 degrees return Vector3.Dot(a, b) < 0f; } } [Serializable] public class SynapseData { public string clusterName; public string neuronName; public float weight; public bool trainable; public SynapseData(Synapse synapse) { this.clusterName = synapse.neuron.parent.name; this.neuronName = synapse.neuron.name; this.weight = synapse.weight; this.trainable = synapse.trainable; } } }