Fix dangling clusters

This commit is contained in:
Pascal Serrarens 2026-06-30 11:37:21 +02:00
parent 737cbfcfe5
commit eb2adaeec3
3 changed files with 51 additions and 6 deletions

View File

@ -19,6 +19,8 @@ namespace NanoBrain.Unity {
view.currentCluster ??= clusterPrefab.cluster;
view.currentNucleus = clusterPrefab.cluster.defaultOutput;
view.selectedOutput = view.currentNucleus;
clusterPrefab.cluster.Cleanup();
}
public override void OnInspectorGUI() {
@ -395,24 +397,26 @@ namespace NanoBrain.Unity {
.Where(synapse => synapse.neuron != null)
.Select(synapse => synapse.neuron);
IEnumerable<Nucleus> nuclei = cluster.cluster.nuclei
.Except(synapseNuclei);
Nucleus[] nuclei = cluster.cluster.nuclei
.Except(synapseNuclei).Distinct().ToArray();
IEnumerable<string> nucleiNames = nuclei
.Select(n => {
int idx = n.name.IndexOf(':');
return idx < 0 ? n.name : n.name[..idx];
})
.Distinct();
});
string[] names = nucleiNames.ToArray();
EditorGUILayout.BeginHorizontal();
selectedConnectNucleus = EditorGUILayout.Popup(selectedConnectNucleus, names);
bool connecting = GUILayout.Button("Connect", GUILayout.Width(80));
EditorGUILayout.EndHorizontal();
if (connecting) {
Nucleus nucleus = nuclei.ElementAt(selectedConnectNucleus);
if (connecting && selectedConnectNucleus >= 0 && selectedConnectNucleus < nuclei.Length) {
Nucleus nucleus = nuclei[selectedConnectNucleus];//nuclei.ElementAt(selectedConnectNucleus);
if (nucleus is Neuron neuron)
neuron.AddReceiver(this.view.currentNucleus);
else if (nucleus is Cluster clusterToConnect) {
clusterToConnect.defaultOutput.AddReceiver(this.view.currentNucleus);
}
this.view.currentCluster.Refresh();
}
return connecting;

View File

@ -711,6 +711,7 @@ namespace NanoBrain {
/// </summary>
/// This can be used to recalculate derived properties after the set of nuclei has been changed
public void Refresh() {
Cleanup();
// This should not be needed, but somehow somewhere the parent is changed...
foreach (Nucleus nucleus in this.nuclei) {
nucleus.parent = this;
@ -719,6 +720,16 @@ namespace NanoBrain {
RefreshComputeOrders();
}
public void Cleanup() {
foreach (Nucleus nucleus in this.nuclei.ToArray()) {
if (nucleus is not Cluster cluster)
continue;
List<Nucleus> receivers = cluster.CollectReceivers();
Debug.Log($"cluster receiver count = {receivers.Count}");
if (receivers.Count == 0)
this.nuclei.Remove(nucleus);
}
}
}
}

View File

@ -363,6 +363,7 @@ namespace NanoBrain
#region Combinator
#if UNITY_MATHEMATICS
[NonSerialized]
public float3 combinationValue;
/// <summary>
@ -742,6 +743,35 @@ namespace NanoBrain
Debug.Log($"Updated weight: {error.magnitude} {error} {scaledOutput} {synapse.weight}");
}
public void BackPropagationWithLoss(Synapse synapse, Vector3 loss, float learningRate)
{
// loss is a derivative of error
// Backpropagation = loss * d(combinator)
Vector3 delta2;
switch (activator)
{
case ActivationType.Linear:
// Derivative of this (f'()) would be 1.
delta2 = loss * 1;
break;
case ActivationType.Power:
delta2 = loss * (2 * this.combinationValue);
break;
case ActivationType.Reciprocal:
delta2 = loss * (-1 / (this.combinationValue * this.combinationValue));
break;
default:
delta2 = loss;
break;
}
Vector3 scaledOutput = Vector3.Scale(delta2, synapse.neuron.outputValue);
float deltaWeight = Mathf.Abs(scaledOutput.x) + Mathf.Abs(scaledOutput.y) + Mathf.Abs(scaledOutput.z);
synapse.weight += learningRate * deltaWeight;
Debug.Log($"Updated weight: {loss.magnitude} {loss} {scaledOutput} {synapse.weight}");
}
#endregion Back propagation
/// <summary>