Initial population persistence

This commit is contained in:
Pascal Serrarens 2026-08-17 09:55:12 +02:00
parent 68b43ee476
commit 5553f8994f
2 changed files with 138 additions and 6 deletions

View File

@ -81,6 +81,8 @@ namespace NanoBrain {
[HideInInspector]
public List<Nucleus> nuclei = new();
#region Timed Actions
public class TimedAction {
public TimedAction(string neuronName, Action action, long timestamp) {
this.neuronName = neuronName;
@ -146,6 +148,8 @@ namespace NanoBrain {
}
}
#endregion Timed Actions
#region Init
public Cluster() {
@ -368,6 +372,90 @@ namespace NanoBrain {
}
}
public Cluster Clone() {
Cluster clone = new() {
name = this.name,
prefab = this.prefab,
version = this.version,
parent = this.parent
};
if (clone.parent != null) {
// Do we need to clone the parent here too????
}
// first clone the nuclei without their connections
foreach (Nucleus nucleus in this.nuclei)
nucleus.ShallowCloneTo(clone);
Nucleus[] clonedNuclei = clone.nuclei.ToArray();
// Now clone the connections
for (int nucleusIx = 0; nucleusIx < this.nuclei.Count; nucleusIx++) {
Nucleus sourceNucleus = this.nuclei[nucleusIx];
if (sourceNucleus is not Neuron sourceNeuron)
continue;
Nucleus clonedNucleus = clonedNuclei[nucleusIx];
if (clonedNucleus == null || clonedNucleus is not Neuron clonedNeuron)
continue;
foreach (Synapse sourceSynapse in sourceNeuron.synapses) {
Neuron synapseNeuron = sourceSynapse.neuron;
if (synapseNeuron.parent.prefab != null && synapseNeuron.parent.prefab != clone.prefab) {
// Neuron is in another cluster, find the cloned cluster first
Cluster sourceCluster = synapseNeuron.parent;
Cluster clonedCluster = clone.nuclei.Find(n => n.name == sourceCluster.name) as Cluster;
if (clonedCluster == null)
continue;
// Now find the neuron in that cloned cluster
int neuronIx = GetNucleusIndex(sourceCluster.nuclei, sourceSynapse.neuron.name);
if (neuronIx < 0)
// Could not find the neuron in the prefab cluster
continue;
if (clonedCluster.nuclei[neuronIx] is not Neuron clonedSender)
// Could not find the neuron in the cloned cluster
continue;
clonedSender.AddReceiver(clonedNeuron, sourceSynapse.weight, sourceSynapse.trainable);
//Debug.Log($"Add synapse {clonedCluster.name}.{clonedSender.name} -> {clonedNeuron.name} [{clonedSender.receivers.Count}]");
}
else {
int ix = GetNucleusIndex(clone.prefab.cluster.nuclei, sourceSynapse.neuron);
if (ix < 0)
continue;
if (clonedNuclei[ix] is not Neuron clonedSender)
continue;
// Copy the receivers which will also create the synapse
clonedSender.AddReceiver(clonedNeuron, sourceSynapse.weight, sourceSynapse.trainable);
// Debug.Log($"Add synapse {clonedSender.name} -> {clonedNeuron.name}");
}
}
}
foreach (Nucleus clonedNucleus in clonedNuclei) {
if (clonedNucleus is not Cluster clonedCluster)
continue;
List<Cluster> siblings = new() { clonedCluster };
for (int instanceIx = 1; instanceIx < clonedCluster.instanceCount; instanceIx++) {
// Create another sibling
Cluster sibling = new(clonedCluster.prefab, this) {
name = $"{clonedCluster.baseName}: {instanceIx}",
parent = clone.parent,
instanceCount = clone.instanceCount,
};
siblings.Add(sibling);
CopyAllExternalReceivers(clonedCluster, sibling, this);
}
Cluster[] siblingClusters = siblings.ToArray();
foreach (Cluster sibling in siblings)
sibling.instances = siblingClusters;
}
return clone;
}
/// \copydoc NanoBrain::Nucleus::ShallowCloneTo
public override Nucleus ShallowCloneTo(Cluster parent) {
// Clusters should not be cloned, but instantiated from the prefab....
@ -1011,10 +1099,10 @@ namespace NanoBrain {
#region Serialization
public void Export(string path) {
ClusterData data = new(this); //this.ToJSON();
ClusterData data = new(this);
string json = JsonUtility.ToJson(data, prettyPrint: true);
// Debug.Log($"Exporting json to {path}");
Debug.Log($"Exporting {this.name} to {path}");
File.WriteAllText(path, json);
}

View File

@ -3,6 +3,9 @@ using UnityEngine;
namespace NanoBrain {
public class Population {
# region Members
public class Member {
public Member(Cluster cluster) {
this.brain = cluster;
@ -21,12 +24,49 @@ namespace NanoBrain {
return newMember;
}
/// <summary>
/// Generational update
/// </summary>
public virtual void Update() {
#endregion Members
#region Saving
public enum SaveMethod {
Average,
Mean,
First,
All
}
public void Save(string path, SaveMethod saveMethod = SaveMethod.Average) {
Cluster cluster = null;
switch (saveMethod) {
case SaveMethod.First:
Member member = this.members[0];
if (member == null)
return;
cluster = member.brain;
break;
case SaveMethod.Average:
cluster = CalculateAverageCluster();
break;
}
cluster?.Export(path);
}
protected Cluster CalculateAverageCluster() {
if (members.Count <= 0)
return null;
Cluster result = members[0].brain.Clone();
for (int memberIx = 1; memberIx < members.Count; memberIx++) {
Member member = members[memberIx];
result.ProcessWeightsFrom(member.brain, Average);
}
return result;
}
#endregion Saving
#region New Generation
public void NewGeneration() {
foreach (Member member in this.members) {
@ -118,6 +158,8 @@ namespace NanoBrain {
return selectedMembers;
}
#endregion New Generation
private void LogTrainableWeights(Cluster cluster) {
string s = "";
foreach (Nucleus nucleus in cluster.nuclei) {
@ -131,5 +173,7 @@ namespace NanoBrain {
}
Debug.Log(s);
}
public virtual void Update() { }
}
}