Deserialization first version

This commit is contained in:
Pascal Serrarens 2026-07-10 12:49:14 +02:00
parent 27f4237890
commit 85017861b5
6 changed files with 118 additions and 53 deletions

View File

@ -15,12 +15,24 @@ namespace NanoBrain.Unity {
void OnEnable() {
clusterPrefab = (ClusterPrefab)target;
clusterPrefab.cluster.name = clusterPrefab.name;
view = ClusterView.GetClusterView(serializedObject);
string assetPath = AssetDatabase.GetAssetPath(clusterPrefab);
if (!string.IsNullOrEmpty(assetPath)) {
string folder = System.IO.Path.GetDirectoryName(assetPath);
string jsonPath = System.IO.Path.Combine(folder, clusterPrefab.name + ".json");
if (System.IO.File.Exists(jsonPath)) {
ClusterData newClusterData = Cluster.Import(jsonPath);
clusterPrefab.cluster = new(newClusterData);
}
}
view.currentCluster ??= clusterPrefab.cluster;
view.currentNucleus = clusterPrefab.cluster.defaultOutput;
view.selectedOutput = view.currentNucleus;
clusterPrefab.cluster.name = clusterPrefab.name;
clusterPrefab.cluster.Cleanup();
}
@ -218,25 +230,25 @@ namespace NanoBrain.Unity {
showSynapses = EditorGUILayout.Foldout(showSynapses, "Synapses", true);
if (showSynapses) {
EditorGUI.indentLevel--;
if (this.view.currentNucleus is Neuron neuron2) {
Neuron.CombinatorType newCombinator = (Neuron.CombinatorType)EditorGUILayout.EnumPopup("Combinator", neuron2.combinator);
anythingChanged |= newCombinator != neuron2.combinator;
neuron2.combinator = newCombinator;
if (this.view.currentNucleus is Neuron neuron) {
Neuron.CombinatorType newCombinator = (Neuron.CombinatorType)EditorGUILayout.EnumPopup("Combinator", neuron.combinator);
anythingChanged |= newCombinator != neuron.combinator;
neuron.combinator = newCombinator;
EditorGUIUtility.wideMode = true;
float previousLabelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 100;
Vector3 newBias = EditorGUILayout.Vector3Field("Bias", neuron2.bias);
if (newBias != neuron2.bias) {
Vector3 newBias = EditorGUILayout.Vector3Field("Bias", neuron.bias);
if (newBias != neuron.bias) {
anythingChanged = true;
neuron2.bias = newBias;
neuron.bias = newBias;
}
bool newTrainable = EditorGUILayout.Toggle("Trainable", neuron2.trainable);
if (newTrainable != neuron2.trainable) {
bool newTrainable = EditorGUILayout.Toggle("Trainable", neuron.trainable);
if (newTrainable != neuron.trainable) {
anythingChanged = true;
neuron2.trainable = newTrainable;
neuron.trainable = newTrainable;
}
EditorGUIUtility.labelWidth = previousLabelWidth;

View File

@ -24,7 +24,7 @@ namespace NanoBrain.Unity {
EditorGUI.BeginChangeCheck();
TextAsset current = jsonFileProp.objectReferenceValue as TextAsset;
Rect fieldRect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
Rect fieldRect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
TextAsset newAsset = (TextAsset)EditorGUI.ObjectField(fieldRect, label, current, typeof(TextAsset), false);
if (EditorGUI.EndChangeCheck()) {
@ -41,7 +41,8 @@ namespace NanoBrain.Unity {
);
propertyExtraHeight = 2 + 2 * EditorGUIUtility.singleLineHeight;
EditorGUI.HelpBox(helpRect, "Selected asset is not a .json file.", MessageType.Error);
} else
}
else
propertyExtraHeight = 0;
EditorGUI.EndProperty();
@ -56,16 +57,19 @@ namespace NanoBrain.Unity {
private readonly Dictionary<EntityId, bool> _invalidCache = new();
private bool IsInvalidJson(SerializedProperty jsonFileProp) {
TextAsset current = jsonFileProp.objectReferenceValue as TextAsset;
if (current == null) return false;
TextAsset jsonFile = jsonFileProp.objectReferenceValue as TextAsset;
if (jsonFile == null)
return false;
EntityId id = current.GetEntityId();
EntityId id = jsonFile.GetEntityId();
if (_invalidCache.TryGetValue(id, out bool invalid))
return invalid;
string path = AssetDatabase.GetAssetPath(current);
string path = AssetDatabase.GetAssetPath(jsonFile);
invalid = !path.EndsWith(".json", System.StringComparison.OrdinalIgnoreCase);
_invalidCache[id] = invalid;
return invalid;
}
}

View File

@ -44,7 +44,7 @@ namespace NanoBrain.Unity {
EditorGUILayout.Space();
scroll = EditorGUILayout.BeginScrollView(scroll);
foreach (var it in items) {
foreach (ClusterPrefab it in this.items) {
if (!string.IsNullOrEmpty(search) && it.name.IndexOf(search, StringComparison.OrdinalIgnoreCase) < 0)
continue;

View File

@ -93,41 +93,41 @@ namespace NanoBrain.Unity {
}
// If a brain has been selected
//if (prefabProp.objectReferenceValue != null) {
// Graph is not shown when multi-editing
if (property.serializedObject.targetObjects.Length == 1) {
UnityEngine.Object targetObject = property.serializedObject.targetObject;
Cluster_Drawer.selectedTarget = targetObject;
if (prefabProp.objectReferenceValue != null) {
// Graph is not shown when multi-editing
if (property.serializedObject.targetObjects.Length == 1) {
UnityEngine.Object targetObject = property.serializedObject.targetObject;
Cluster_Drawer.selectedTarget = targetObject;
Cluster cluster = SerializedPropertyUtility.GetManagedObjectForProperty(targetObject, property.propertyPath) as Cluster;
if (cluster != null) {
// if (cluster.version != cluster.prefab.version) {
// // Debug.Log($"prefab version: {cluster.prefab.version} cluster version: {cluster.version}");
// clusterView.initialized = false;
// EditorApplication.delayCall += () => InstantiateCluster(property, clusterView);
// }
// foldout header rect
Rect headerRect = new(fieldRect.x, fieldRect.yMax + 4f, fieldRect.width, EditorGUIUtility.singleLineHeight);
clusterView.isOpen = EditorGUI.Foldout(headerRect, clusterView.isOpen, "Graph", true);
if (clusterView.isOpen) {
// content rect below header
Rect drawRect = new(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f);
if (cluster is not null && (clusterView.currentCluster == null || clusterView.currentCluster != cluster)) {
clusterView.currentCluster = cluster;
clusterView.currentNucleus = cluster.defaultOutput;
clusterView.selectedOutput = clusterView.currentNucleus;
Cluster cluster = SerializedPropertyUtility.GetManagedObjectForProperty(targetObject, property.propertyPath) as Cluster;
if (cluster != null) {
if (cluster.version != cluster.prefab.version) {
// Debug.Log($"prefab version: {cluster.prefab.version} cluster version: {cluster.version}");
clusterView.initialized = false;
EditorApplication.delayCall += () => InstantiateCluster(property, clusterView);
}
Cluster_Drawer.currentClusterView = clusterView;
clusterView.Render(drawRect);
//Debug.Log(prefab.cluster.defaultOutput.outputMagnitude);
}
// foldout header rect
Rect headerRect = new(fieldRect.x, fieldRect.yMax + 4f, fieldRect.width, EditorGUIUtility.singleLineHeight);
clusterView.isOpen = EditorGUI.Foldout(headerRect, clusterView.isOpen, "Graph", true);
if (clusterView.isOpen) {
// content rect below header
Rect drawRect = new(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f);
if (cluster is not null && (clusterView.currentCluster == null || clusterView.currentCluster != cluster)) {
clusterView.currentCluster = cluster;
clusterView.currentNucleus = cluster.defaultOutput;
clusterView.selectedOutput = clusterView.currentNucleus;
}
Cluster_Drawer.currentClusterView = clusterView;
clusterView.Render(drawRect);
//Debug.Log(prefab.cluster.defaultOutput.outputMagnitude);
}
}
}
}
//}
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();

View File

@ -25,6 +25,7 @@ namespace NanoBrain {
/// </summary>
/// Cluster should always be created from prefabs
public ClusterPrefab prefab;
public ClusterJson json;
//[HideInInspector]
@ -129,6 +130,46 @@ namespace NanoBrain {
}
}
}
public Cluster(ClusterData clusterData) {
this.name = clusterData.name;
// First create all neurons
foreach (NeuronData neuronData in clusterData.neurons) {
if (neuronData.type == Type.MemoryCell)
new MemoryCell(this, neuronData.name) {
bias = neuronData.bias,
combinator = neuronData.combinatorType,
activator = neuronData.activationType
};
else
new Neuron(this, neuronData.name) {
bias = neuronData.bias,
combinator = neuronData.combinatorType,
activator = neuronData.activationType
};
}
// Now create the synapses between them
foreach (NeuronData neuronData in clusterData.neurons) {
Neuron receiver = this.GetNeuron(neuronData.name);
foreach (SynapseData synapseData in neuronData.synapses) {
if (synapseData.clusterName != this.name) {
// Add reference to external cluster
ClusterPrefab extPrefab = Resources.Load(synapseData.clusterName) as ClusterPrefab;
if (extPrefab == null)
Debug.LogError($"Could not find cluster Resource {synapseData.clusterName}");
else {
Cluster extCluster = new Cluster(extPrefab, this);
Neuron extNeuron = extCluster.GetNeuron(synapseData.neuronName);
extNeuron.AddReceiver(receiver);
}
}
else {
Neuron sender = this.GetNeuron(synapseData.neuronName);
sender?.AddReceiver(receiver, synapseData.weight);
}
}
}
}
/// <summary>
@ -804,8 +845,9 @@ namespace NanoBrain {
System.IO.File.WriteAllText(path, json);
}
public ClusterData Import(string path) {
public static ClusterData Import(string path) {
string json = File.ReadAllText(path);
Debug.Log($"Importing json from {path}");
return JsonUtility.FromJson<ClusterData>(json);
}
@ -824,10 +866,12 @@ namespace NanoBrain {
if (nucleus is Neuron neuron) {
NeuronData neuronData = new(neuron);
this.neurons.Add(neuronData);
}
else if (nucleus is Cluster extCluster) {
ExternalClusterData clusterData = new(extCluster);
this.clusters.Add(clusterData);
foreach (Synapse synapse in neuron.synapses) {
if (synapse.neuron.parent.name != cluster.name) {
ExternalClusterData clusterData = new(synapse.neuron.parent);
this.clusters.Add(clusterData);
}
}
}
}
}

View File

@ -737,12 +737,17 @@ namespace NanoBrain {
[Serializable]
public class NeuronData {
public string name;
public Nucleus.Type type;
public Vector3 bias = Vector3.zero;
public Neuron.CombinatorType combinatorType;
public List<SynapseData> synapses = new();
public Neuron.ActivationType activationType;
public NeuronData(Neuron neuron) {
if (neuron is MemoryCell)
this.type = Nucleus.Type.MemoryCell;
else
this.type = Nucleus.Type.Neuron;
this.name = neuron.name;
this.bias = neuron.bias;
this.combinatorType = neuron.combinator;