From e212edb8084adfc6a08aa1de9e70bd8eb1fcd42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Slabber?= Date: Wed, 8 Jul 2026 08:33:00 +0200 Subject: [PATCH 1/2] Various fixes for problems identified by Grok This fork contains the first set of fixes to BrainSimThought code for errors that were found by Grok --- BrainSimMAC/BrainSimMACMain.cs | 21 +- BrainSimulator/BrainSim Thought.csproj | 22 +- BrainSimulator/GlobalUsings.cs | 1 + BrainSimulator/MainWindowFiles.cs | 4 +- BrainSimulator/ModuleHandler.cs | 14 +- BrainSimulator/ModuleViewMenu.cs | 32 +- .../Modules/Agents/ModuleAttributeBubble.cs | 15 +- ...lg.xml.cs => ModuleBalanceTreeDlg.xaml.cs} | 2 +- BrainSimulator/Modules/ModuleGPTInfo.cs | 2 + BrainSimulator/Modules/ModuleLearnMelody.cs | 16 +- BrainSimulator/Modules/ModuleOnlineInfo.cs | 186 +- .../Modules/ModuleUKSQueryDlg.xaml.cs | 3 +- .../Vision/ModuleMentalModelDlg.xaml.cs | 5 +- BrainSimulator/Modules/Vision/ModuleVision.cs | 4 +- .../Modules/Vision/ModuleVisionDlg.xaml.cs | 6 +- BrainSimulator/Modules/Vision/README.md | 12 + BrainSimulator/Network.cs | 14 +- BrainSimulator/Resources/wordlist.txt | 30 + BrainSimulator/UKSContent/NatureAnimals.xml | 1995 +++++++++++++++++ BrainSimulator/Utils.cs | 23 +- .../archive/legacy/MainWindowPythonModules.cs | 173 ++ BrainSimulator/archive/legacy/README.md | 10 + .../{ => archive/legacy}/XmlFile.cs | 0 PythonProj/MainWindow.py | 28 +- PythonProj/module_uks_tree.py | 8 +- PythonProj/utils.py | 29 +- Tests/ModuleAlgorithm.Tests.cs | 2 +- Tests/NatureAnimalsXmlTests.cs | 87 + Tests/NonUksP0FixesRegressionTests.cs | 38 + Tests/NonUksP1FixesRegressionTests.cs | 39 + Tests/NonUksP2FixesRegressionTests.cs | 52 + Tests/NonUksRemainingFixesRegressionTests.cs | 56 + Tests/UKSFixesRegressionTests.cs | 117 + Tests/VisionCompatRegressionTests.cs | 52 + ...xtensiion.cs => IReadOnlyListExtension.cs} | 0 UKS/Relationship.cs | 33 + UKS/Thought.BrainSim3Compat.cs | 104 + UKS/Thought.cs | 76 +- UKS/UKS .Initialize.cs | 2 +- UKS/UKS.BrainSim3Compat.cs | 102 + UKS/UKS.Exclusivity.cs | 8 +- UKS/UKS.Sequence.cs | 33 +- UKS/UKS.Statement.cs | 45 +- UKS/UKS.TextFile.cs | 31 +- UKS/UKS.XMLFile.cs | 31 +- tools/generate_nature_animals_xml.py | 359 +++ 46 files changed, 3636 insertions(+), 286 deletions(-) create mode 100644 BrainSimulator/GlobalUsings.cs rename BrainSimulator/Modules/Agents/{ModuleBalanceTreeDlg.xml.cs => ModuleBalanceTreeDlg.xaml.cs} (99%) create mode 100644 BrainSimulator/Modules/Vision/README.md create mode 100644 BrainSimulator/Resources/wordlist.txt create mode 100644 BrainSimulator/UKSContent/NatureAnimals.xml create mode 100644 BrainSimulator/archive/legacy/MainWindowPythonModules.cs create mode 100644 BrainSimulator/archive/legacy/README.md rename BrainSimulator/{ => archive/legacy}/XmlFile.cs (100%) create mode 100644 Tests/NatureAnimalsXmlTests.cs create mode 100644 Tests/NonUksP0FixesRegressionTests.cs create mode 100644 Tests/NonUksP1FixesRegressionTests.cs create mode 100644 Tests/NonUksP2FixesRegressionTests.cs create mode 100644 Tests/NonUksRemainingFixesRegressionTests.cs create mode 100644 Tests/UKSFixesRegressionTests.cs create mode 100644 Tests/VisionCompatRegressionTests.cs rename UKS/{IReadOnlyListExtensiion.cs => IReadOnlyListExtension.cs} (100%) create mode 100644 UKS/Relationship.cs create mode 100644 UKS/Thought.BrainSim3Compat.cs create mode 100644 UKS/UKS.BrainSim3Compat.cs create mode 100644 tools/generate_nature_animals_xml.py diff --git a/BrainSimMAC/BrainSimMACMain.cs b/BrainSimMAC/BrainSimMACMain.cs index f7f1655..0d3d04e 100644 --- a/BrainSimMAC/BrainSimMACMain.cs +++ b/BrainSimMAC/BrainSimMACMain.cs @@ -5,12 +5,13 @@ * * This file is part of Brain Simulator Thought and is licensed under * the MIT License. You may use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of this software under the terms of + * sublicense, and/or sell copies of the software under the terms of * the MIT License. * * See the LICENSE file in the project root for full license information. */ using BrainSimulator; +using System.Diagnostics; using UKS; @@ -67,15 +68,22 @@ //force the MainWindow to always be activated moduleHandler.ActivateModule("MainWindow.py"); +const int loopDelayMs = 50; while (true) { + activeModulesRoot = moduleHandler.theUKS.Labeled("ActiveModule"); + if (activeModulesRoot is null) + { + Thread.Sleep(loopDelayMs); + continue; + } + foreach (var module in activeModulesRoot.Children) { if (module.Label.Contains(".py")) moduleHandler.RunScript(module.Label); } - activeModulesRoot = moduleHandler.theUKS.Labeled("ActiveModule"); for (int i = 0; i < moduleHandler.activePythonModules.Count; i++) { @@ -88,7 +96,12 @@ moduleHandler.activePythonModules.RemoveAt(i); i--; } - catch { } + catch (Exception ex) + { + Debug.WriteLine($"BrainSimMAC: Close failed for {module.Item1}: {ex.Message}"); + } } } -} + + Thread.Sleep(loopDelayMs); +} \ No newline at end of file diff --git a/BrainSimulator/BrainSim Thought.csproj b/BrainSimulator/BrainSim Thought.csproj index 94e72b8..676960f 100644 --- a/BrainSimulator/BrainSim Thought.csproj +++ b/BrainSimulator/BrainSim Thought.csproj @@ -25,9 +25,10 @@ preview + - + @@ -49,7 +50,11 @@ - + + + + + @@ -74,6 +79,7 @@ + @@ -123,6 +129,11 @@ PreserveNewest + + + PreserveNewest + + PreserveNewest @@ -131,11 +142,6 @@ PreserveNewest - - - - - - + \ No newline at end of file diff --git a/BrainSimulator/GlobalUsings.cs b/BrainSimulator/GlobalUsings.cs new file mode 100644 index 0000000..c48ada4 --- /dev/null +++ b/BrainSimulator/GlobalUsings.cs @@ -0,0 +1 @@ +global using Thing = UKS.Thought; \ No newline at end of file diff --git a/BrainSimulator/MainWindowFiles.cs b/BrainSimulator/MainWindowFiles.cs index 4e7b79a..50aea99 100644 --- a/BrainSimulator/MainWindowFiles.cs +++ b/BrainSimulator/MainWindowFiles.cs @@ -63,10 +63,10 @@ public void ReloadActiveModulesSP() ActiveModuleSP.Children.Clear(); Thought activeModuleParent = theUKS.Labeled("ActiveModule"); + if (activeModuleParent is null) { return; } + //TODO: Remove activeModuleParent.AddParent("BrainSim"); - - if (activeModuleParent is null) { return; } var activeModules1 = activeModuleParent.Children; activeModules1 = activeModules1.OrderBy(x => x.Label).ToList(); diff --git a/BrainSimulator/ModuleHandler.cs b/BrainSimulator/ModuleHandler.cs index f0cf3ef..7585b94 100644 --- a/BrainSimulator/ModuleHandler.cs +++ b/BrainSimulator/ModuleHandler.cs @@ -69,14 +69,7 @@ public void DeactivateModule(string moduleLabel) { Thought t = theUKS.Labeled(moduleLabel); if (t is null) return; - for (int i = 0; i < t.LinksTo.Count; i++) - { - Link r = t.LinksTo[i]; - r.To.Delete(); - } t.Delete(); - - return; } @@ -141,7 +134,7 @@ public void Close(string moduleLabel) { theModuleEntry.Item2.Close(); } - catch { } + catch (Exception ex) { Debug.WriteLine($"Python Close failed for {theModuleEntry.Item1}: {ex.Message}"); } } } } @@ -221,9 +214,10 @@ public void RunScript(string moduleLabel) catch (Exception ex) { activePythonModules.Remove(theModuleEntry); - DeactivateModule(moduleLabel); #if !CONSOLE_APP - MainWindow.theWindow.ReloadActiveModulesSP(); + MainWindow.theWindow?.DeactivateModule(moduleLabel); +#else + DeactivateModule(moduleLabel); #endif Console.WriteLine("Fire method call failed for module: " + moduleLabel + " Reason: " + ex.Message); } diff --git a/BrainSimulator/ModuleViewMenu.cs b/BrainSimulator/ModuleViewMenu.cs index 20ded0d..5103beb 100644 --- a/BrainSimulator/ModuleViewMenu.cs +++ b/BrainSimulator/ModuleViewMenu.cs @@ -100,17 +100,16 @@ private void Mi_Click(object sender, RoutedEventArgs e) if ((string)mi.Header == "View Dialog Source") theModuleType += "Dlg.xaml"; - string cwd = System.IO.Directory.GetCurrentDirectory(); - if (cwd.Contains("bin\\")) - cwd = cwd.ToLower().Substring(0, cwd.IndexOf("bin\\")); - string fileName = cwd + @"modules\" + theModuleType + ".cs"; + string cwd = Directory.GetCurrentDirectory(); + string binSegment = "bin" + Path.DirectorySeparatorChar; + int binIndex = cwd.IndexOf(binSegment, StringComparison.OrdinalIgnoreCase); + if (binIndex >= 0) + cwd = cwd.Substring(0, binIndex); + string fileName = Path.Combine(cwd, "modules", theModuleType + ".cs"); + if (!File.Exists(fileName)) + fileName = Path.Combine(cwd, "BrainSim2modules", theModuleType + ".cs"); if (File.Exists(fileName)) OpenSource(fileName); - else - { - fileName = cwd + @"BrainSim2modules\" + theModuleType + ".cs"; - OpenSource(fileName); - } } if ((string)mi.Header == "Delete") { @@ -176,5 +175,20 @@ public void DeleteModule(string moduleName) ReloadActiveModulesSP(); } + + public void DeactivateModule(string moduleLabel) + { + ModuleBase mb = activeModules.FindFirst(x => x.Label == moduleLabel); + if (mb is not null) + { + mb.CloseDlg(); + mb.Closing(); + activeModules.Remove(mb); + } + pythonModules.Remove(moduleLabel); + moduleHandler.pythonModules.Remove(moduleLabel); + moduleHandler.DeactivateModule(moduleLabel); + ReloadActiveModulesSP(); + } } } diff --git a/BrainSimulator/Modules/Agents/ModuleAttributeBubble.cs b/BrainSimulator/Modules/Agents/ModuleAttributeBubble.cs index d0f782b..c293f63 100644 --- a/BrainSimulator/Modules/Agents/ModuleAttributeBubble.cs +++ b/BrainSimulator/Modules/Agents/ModuleAttributeBubble.cs @@ -155,15 +155,16 @@ void BubbleChildAttributes(Thought t) } - //calculate the new weight - //If there is an existing weight, it is increased/decreased by a small amound and removed if it drops below .5 - //If there is no existing weight, it is assumed to start at 0.5. - //TODO, replace this hardcoded "lookup table" with a formula + // Weight delta ladder: maps (positiveWeight - negativeWeight) to a small adjustment. + // Thresholds tuned empirically; initial weight defaults to 0.5 when absent; links below 0.5 are removed. float targetWeight = 0; float deltaWeight = positiveWeight - negativeWeight; - if (deltaWeight < .8) targetWeight = -.1f; - else if (deltaWeight < 1.7) targetWeight = .01f; - else if (deltaWeight < 2.7) targetWeight = .2f; + const float weakDeltaThreshold = 0.8f; + const float moderateDeltaThreshold = 1.7f; + const float strongDeltaThreshold = 2.7f; + if (deltaWeight < weakDeltaThreshold) targetWeight = -.1f; + else if (deltaWeight < moderateDeltaThreshold) targetWeight = .01f; + else if (deltaWeight < strongDeltaThreshold) targetWeight = .2f; else targetWeight = .3f; if (currentWeight == 0) currentWeight = 0.5f; float newWeight = currentWeight + targetWeight; diff --git a/BrainSimulator/Modules/Agents/ModuleBalanceTreeDlg.xml.cs b/BrainSimulator/Modules/Agents/ModuleBalanceTreeDlg.xaml.cs similarity index 99% rename from BrainSimulator/Modules/Agents/ModuleBalanceTreeDlg.xml.cs rename to BrainSimulator/Modules/Agents/ModuleBalanceTreeDlg.xaml.cs index ac8a5c1..8dbeb30 100644 --- a/BrainSimulator/Modules/Agents/ModuleBalanceTreeDlg.xml.cs +++ b/BrainSimulator/Modules/Agents/ModuleBalanceTreeDlg.xaml.cs @@ -56,4 +56,4 @@ private void Enable_Checked(object sender, RoutedEventArgs e) } } } -} +} \ No newline at end of file diff --git a/BrainSimulator/Modules/ModuleGPTInfo.cs b/BrainSimulator/Modules/ModuleGPTInfo.cs index c7bc2fa..01ff081 100644 --- a/BrainSimulator/Modules/ModuleGPTInfo.cs +++ b/BrainSimulator/Modules/ModuleGPTInfo.cs @@ -123,6 +123,8 @@ public static async Task GetChatGPTVerifyParentChild(string child,string parent) //this turns dotted names back into more english-language strings private static string GetStringFromThoughtLabel(string thoughtLabel) { + if (string.IsNullOrEmpty(thoughtLabel)) + return ""; string theString = thoughtLabel.ToLower(); if (theString[0] == '.') theString = theString.Substring(1); string[] s = theString.Split('.'); diff --git a/BrainSimulator/Modules/ModuleLearnMelody.cs b/BrainSimulator/Modules/ModuleLearnMelody.cs index d95b028..e9db041 100644 --- a/BrainSimulator/Modules/ModuleLearnMelody.cs +++ b/BrainSimulator/Modules/ModuleLearnMelody.cs @@ -119,7 +119,7 @@ private void ProcessMelodyEntry(MelodyEntry entry) SeqElement seq2 = CreatePhraseFromNotes("temp*", entry.ResponsePhrase.Notes); foreach (Thought t in theUKS.Labeled("possibleAction").Children) { - SeqElement seq3 = GetTargetOfFirstLinkOfType(t, "soundAs"); + SeqElement seq3 = t.GetTargetOfFirstLinkOfType("soundAs") as SeqElement; var val = theUKS.CompareSequences(seq2, seq3); if (val == 1) { @@ -151,20 +151,6 @@ public bool ResponseHandled (Thought theResponse) return true; } - //TODO Move to UKS - public SeqElement GetTargetOfFirstLinkOfType(Thought thePhrase, string v) - { - foreach (var link in thePhrase.LinksTo) - { - if (link.LinkType.Label == v && link.To is SeqElement seq) - { - return seq; - } - } - return null; - } - - // Randomize the melody lines private void RandomizeMelodies() { diff --git a/BrainSimulator/Modules/ModuleOnlineInfo.cs b/BrainSimulator/Modules/ModuleOnlineInfo.cs index 8895fb1..a0a7924 100644 --- a/BrainSimulator/Modules/ModuleOnlineInfo.cs +++ b/BrainSimulator/Modules/ModuleOnlineInfo.cs @@ -24,6 +24,7 @@ using System.Text; using System.Text.Json; using System.Text.RegularExpressions; +using System.Threading.Tasks; using System.Xml; using System.Linq; using Pluralize.NET; @@ -70,10 +71,11 @@ public override void Fire() if (wordsToLookUp.Count > 0) { - string word = wordsToLookUp[0].Item1; - int mostUsed = wordsToLookUp.FindIndex(t => t.Item2 == wordsToLookUp.Max(t => t.Item2)); - var x = wordsToLookUp[0]; - wordsToLookUp.RemoveAt(0); + int maxUse = wordsToLookUp.Max(t => t.Item2); + int mostUsed = wordsToLookUp.FindIndex(t => t.Item2 == maxUse); + if (mostUsed < 0) mostUsed = 0; + var x = wordsToLookUp[mostUsed]; + wordsToLookUp.RemoveAt(mostUsed); ConceptNetLocal(x.Item1); } @@ -473,29 +475,9 @@ private static void GetValueFromURI(ref string URI, ref string partOfSpeech) URI = URI.Substring(URI.LastIndexOf("/") + 1); } - public async void GetConceptNetData(string text) + public void GetConceptNetData(string text) { ConceptNetLocal(text); - return; - var url = @"https://api.conceptnet.io/c/en/" + text; - var myClient = new HttpClient(); - var responseURL = await myClient.GetAsync(url); - var propertyURL = await responseURL.Content.ReadAsStringAsync(); - Root myDeserializedClass = JsonConvert.DeserializeObject(propertyURL); - Output = ""; - foreach (var edge in myDeserializedClass.edges) - { - if (edge.start.language != "en") continue; - if (edge.end.language != "en") continue; - string start = GetTailOfURL(edge.start.term); - string end = GetTailOfURL(edge.end.term); - string rel = edge.rel.label; - if (rel == "IsA") - Output += start + "->is-a-> " + end + " " + edge.surfaceText + " " + edge.weight.ToString("f3") + "\n"; - else - Output += start + "->" + rel + "-> " + end + " " + edge.surfaceText + " " + edge.weight.ToString("f3") + "\n"; - } - return; } string GetTailOfURL(string url) { @@ -506,22 +488,45 @@ string GetTailOfURL(string url) List<(string, string)> wordList2 = new(); List wordList = new List(); + private static string ResolveWordListPath() + { + var candidates = new List(); + string envPath = Environment.GetEnvironmentVariable("WORDLIST_PATH"); + if (!string.IsNullOrWhiteSpace(envPath)) + candidates.Add(envPath); + candidates.Add(Path.Combine(AppContext.BaseDirectory, "Resources", "wordlist.txt")); + candidates.Add(Path.Combine(Directory.GetCurrentDirectory(), "Resources", "wordlist.txt")); + candidates.Add(Path.Combine(Directory.GetCurrentDirectory(), "wordlist.txt")); + + foreach (string path in candidates) + { + if (!string.IsNullOrWhiteSpace(path) && File.Exists(path)) + return path; + } + return null; + } + private void SetupWordList() { wordList = new(); + string wordListPath = ResolveWordListPath(); + if (wordListPath is null) + { + Debug.WriteLine("SetupWordList: no wordlist found. Set WORDLIST_PATH or deploy Resources/wordlist.txt"); + return; + } try { - - // also a possible resource Vocabulary.com - - // common words.pdf - ///https://cehs.unl.edu/documents/secd/aac/vocablists/VLN1.pdf - string wordListPath = @"C:\Users\c_sim\source\wordlist.txt"; using (StreamReader reader = new StreamReader(wordListPath)) { string line = reader.ReadLine(); while (line is not null) { + if (line.TrimStart().StartsWith("#")) + { + line = reader.ReadLine(); + continue; + } line = line.Replace("\t", " "); string[] words = line.Split(" "); foreach (string s in words) @@ -601,7 +606,7 @@ public void SetupWordList2(string wordIn = "") word = word.Replace(",", ""); word = word.Replace("\t", " "); word = word.Replace("-", ""); - word = word.Replace("’", ""); + word = word.Replace("�", ""); word = word.ToLower(); word = RemoveParentheticals(word); //word = Thought.TrimDigits(word.Trim()); @@ -649,20 +654,24 @@ public void SetupWordList2(string wordIn = "") } } - //TODO: properly handle phrases, related, similar...currently only picks up first instance - public class KidsWord { public string word; public List definitions = new(); - public string related; - public string similar; - public string phrase; + public List related = new(); + public List similar = new(); + public List phrases = new(); public override string ToString() { string retVal = word + "\n"; foreach (var def in definitions) retVal += def.definition + "\n"; + if (phrases.Count > 0) + retVal += "phrases: " + string.Join(", ", phrases) + "\n"; + if (similar.Count > 0) + retVal += "similar: " + string.Join(", ", similar) + "\n"; + if (related.Count > 0) + retVal += "related: " + string.Join(", ", related) + "\n"; return retVal; } } @@ -695,7 +704,7 @@ public void GetKidsDefinition(string text) return; } - public async void GetKidsWordsmythNet(string text) + public async Task GetKidsWordsmythNet(string text) { var client = new HttpClient(); var response = await client.GetAsync($"https://kids.wordsmyth.net/we/?ent={text}"); @@ -756,13 +765,10 @@ int GetDefinition(int index) Thought incomingInfo = theUKS.GetOrAddThought("CurrentIncomingInfo", "Attention"); incomingInfo.V = kidsdef; + CollectStringValues(htmlContent, "phrase:", kidsdef.phrases, posSearch, "<"); + CollectStringValues(htmlContent, "similar words:", kidsdef.similar, posSearch, "<"); + CollectStringValues(htmlContent, "related words:", kidsdef.related, posSearch, "<"); int index = 0; - kidsdef.phrase = GetStringValue(htmlContent, "phrase:", ref index, posSearch, "<"); - index = 0; - kidsdef.similar = GetStringValue(htmlContent, "similar words:", ref index, posSearch, "<"); - index = 0; - kidsdef.related = GetStringValue(htmlContent, "related words:", ref index, posSearch, "<"); - index = 0; string retString = ""; index = 0; do @@ -789,6 +795,31 @@ string GetStringValue(string content, string target, ref int startIndex, string return retVal; } + private static void CollectStringValues(string content, string target, List dest, string startTag, string endTag) + { + int index = 0; + string val; + while ((val = GetStringValueStatic(content, target, ref index, startTag, endTag)) != "") + { + if (!dest.Contains(val)) + dest.Add(val); + } + } + + private static string GetStringValueStatic(string content, string target, ref int startIndex, string startTag, string endTag) + { + int i1 = content.IndexOf(target, startIndex); + if (i1 == -1) return ""; + i1 = content.IndexOf(startTag, i1); + if (i1 == -1) return ""; + i1 += startTag.Length; + int i2 = content.IndexOf(endTag, i1); + if (i2 == -1) return ""; + string retVal = content.Substring(i1, i2 - i1); + startIndex = i2; + return retVal; + } + public List FindAllIndices(string str, string substr) { var indices = new List(); @@ -803,7 +834,7 @@ public List FindAllIndices(string str, string substr) return indices; } - public async void GetWiktionaryData(string textIn) + public async Task GetWiktionaryData(string textIn) { var word = textIn; // the word to look up @@ -838,7 +869,7 @@ public async void GetWiktionaryData(string textIn) } public enum QueryType { general, isa, hasa, can, count, list, listCount, types, partsOf }; - public async void GetChatGPTResult(string textIn, QueryType qtIn = QueryType.isa, string altLabel = "") + public async Task GetChatGPTResult(string textIn, QueryType qtIn = QueryType.isa, string altLabel = "") { try { @@ -930,9 +961,10 @@ string PluralizePhrase(string input) //This method rerieves the wikidata query number value from wikidata query // for the Thought item and passes that value to GetPropertiesFromURL along // with the property query named prop - public async void GetWikidataData(string item, string prop) + public async Task GetWikidataData(string item, string prop) { var itemLabel = item; + bool handedOff = false; try { Network.httpClientBusy = true; @@ -941,32 +973,45 @@ public async void GetWikidataData(string item, string prop) "&language=en&format=xml"; var response = await Network.theHttpClient.GetAsync(url); - if (response is not null) + if (response is null || !response.IsSuccessStatusCode) + return; + + string xmlContent = await response.Content.ReadAsStringAsync(); + XmlDocument xmlItemDoc = new XmlDocument(); + xmlItemDoc.LoadXml(xmlContent); + var xmlItemDocValue = xmlItemDoc.GetElementsByTagName("entity"); + if (xmlItemDocValue.Count == 0) { - var content = response.Content.ReadAsStringAsync(); - XmlDocument xmlItemDoc = new XmlDocument(); - xmlItemDoc.LoadXml(content.Result.ToString()); - var xmlItemDocValue = xmlItemDoc.GetElementsByTagName("entity"); - var xmlItemDocValueFirst = xmlItemDocValue[0]; - if (xmlItemDocValueFirst is not null) - { - var nameLabel = xmlItemDocValueFirst.Attributes[0]; - string itemID = nameLabel.InnerXml; - Network.httpClientBusy = false; - GetPropertiesFromURL(itemID, prop);/// - } + Debug.WriteLine($"GetWikidataData: no entity for '{itemLabel}'."); + return; + } + var xmlItemDocValueFirst = xmlItemDocValue[0]; + if (xmlItemDocValueFirst?.Attributes?.Count > 0) + { + var nameLabel = xmlItemDocValueFirst.Attributes[0]; + string itemID = nameLabel.InnerXml; + handedOff = true; + await GetPropertiesFromURL(itemID, prop); } } - catch { } - + catch (Exception ex) + { + Debug.WriteLine($"GetWikidataData failed for '{itemLabel}': {ex.Message}"); + } + finally + { + if (!handedOff) + Network.httpClientBusy = false; + } } //This method gets all the properties or subproperties of a property propName from a Thought // with name propName and wikidata query number value numberOfName associated with propName - private async void GetPropertiesFromURL(string itemID, string propName) + private async Task GetPropertiesFromURL(string itemID, string propName) { + try + { Output = ""; propName = propName.ToLower(); - Network.httpClientBusy = true; var urlBegin = @"https://query.wikidata.org/sparql?query="; var url = @"SELECT ?wdLabel ?ps_Label ?wdpqLabel ?pq_Label " + @"{VALUES(?company) { (wd:" + itemID + @")} " + @@ -1094,6 +1139,15 @@ private async void GetPropertiesFromURL(string itemID, string propName) } } } + } + catch (Exception ex) + { + Debug.WriteLine($"GetPropertiesFromURL failed for '{itemID}': {ex.Message}"); + } + finally + { + Network.httpClientBusy = false; + } } @@ -1208,7 +1262,7 @@ public class View } - public async void GetFreeDictionaryAPIData(string text) + public async Task GetFreeDictionaryAPIData(string text) { var url = @"https://api.dictionaryapi.dev/api/v2/entries/en/" + text; var myClient = new HttpClient(); @@ -1272,7 +1326,7 @@ public class Root1 } - public async void GetWebstersDictionaryAPIData(string text) + public async Task GetWebstersDictionaryAPIData(string text) { var url = @"https://dictionaryapi.com/api/v3/references/sd2/json/" + text + "?key=a22c5742-ad8e-44b4-b4c4-9f3f9ac7aedb"; var myClient = new HttpClient(); diff --git a/BrainSimulator/Modules/ModuleUKSQueryDlg.xaml.cs b/BrainSimulator/Modules/ModuleUKSQueryDlg.xaml.cs index 81f33a9..d290219 100644 --- a/BrainSimulator/Modules/ModuleUKSQueryDlg.xaml.cs +++ b/BrainSimulator/Modules/ModuleUKSQueryDlg.xaml.cs @@ -150,7 +150,8 @@ private void QueryByAttributes() resultText1.Text += result1.t.Label + " " + result1.conf.ToString("0.00") + "\n"; } - if (allResults.Count == 1 || allResults[0].conf > allResults[1].conf) + if (allResults.Count > 0 && + (allResults.Count == 1 || allResults[0].conf > allResults[1].conf)) { UpdateMostRecent(allResults[0].t); } diff --git a/BrainSimulator/Modules/Vision/ModuleMentalModelDlg.xaml.cs b/BrainSimulator/Modules/Vision/ModuleMentalModelDlg.xaml.cs index afae1db..4ae470f 100644 --- a/BrainSimulator/Modules/Vision/ModuleMentalModelDlg.xaml.cs +++ b/BrainSimulator/Modules/Vision/ModuleMentalModelDlg.xaml.cs @@ -13,13 +13,14 @@ // // PROPRIETARY AND CONFIDENTIAL // Brain Simulator 3 v.1.0 -// © 2022 FutureAI, Inc., all rights reserved +// � 2022 FutureAI, Inc., all rights reserved // using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; +using BrainSimulator; using UKS; namespace BrainSimulator.Modules @@ -99,7 +100,7 @@ public override bool Draw(bool checkDrawTimer) curPos += offset; poly.Points.Add(curPos); } - catch { } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Mental model distance parse failed: {ex.Message}"); } } } if (poly.Points.Count < 2) //there is no recenetly-refreshed data, use the stored shape diff --git a/BrainSimulator/Modules/Vision/ModuleVision.cs b/BrainSimulator/Modules/Vision/ModuleVision.cs index 6c92528..9a1134d 100644 --- a/BrainSimulator/Modules/Vision/ModuleVision.cs +++ b/BrainSimulator/Modules/Vision/ModuleVision.cs @@ -13,7 +13,7 @@ // // PROPRIETARY AND CONFIDENTIAL // Brain Simulator 3 v.1.0 -// © 2022 FutureAI, Inc., all rights reserved +// � 2022 FutureAI, Inc., all rights reserved // using System; @@ -563,7 +563,7 @@ private void FindOutlines() Thing theColor = GetOrAddColor(theCenterColor); currOutline.SetAttribute(theColor); } - catch (Exception e) { } + catch (Exception e) { System.Diagnostics.Debug.WriteLine($"Centroid color read failed: {e.Message}"); } //we now have an ordered, right-handed outline //add it to UKS diff --git a/BrainSimulator/Modules/Vision/ModuleVisionDlg.xaml.cs b/BrainSimulator/Modules/Vision/ModuleVisionDlg.xaml.cs index 49a6414..63d5e06 100644 --- a/BrainSimulator/Modules/Vision/ModuleVisionDlg.xaml.cs +++ b/BrainSimulator/Modules/Vision/ModuleVisionDlg.xaml.cs @@ -13,7 +13,7 @@ // // PROPRIETARY AND CONFIDENTIAL // Brain Simulator 3 v.1.0 -// © 2022 FutureAI, Inc., all rights reserved +// � 2022 FutureAI, Inc., all rights reserved // using System; @@ -56,7 +56,7 @@ public override bool Draw(bool checkDrawTimer) "\r\nCorners: " + parent.corners?.Count + "\r\nOutlines: " + parent.theUKS.Labeled("Outline")?.Children.Count; } - catch { return false; } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Vision dialog label update failed: {ex.Message}"); return false; } theCanvas.Children.Clear(); @@ -287,7 +287,7 @@ public override bool Draw(bool checkDrawTimer) } } } - catch { } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Vision dialog draw failed: {ex.Message}"); } return true; } diff --git a/BrainSimulator/Modules/Vision/README.md b/BrainSimulator/Modules/Vision/README.md new file mode 100644 index 0000000..3ef1ab7 --- /dev/null +++ b/BrainSimulator/Modules/Vision/README.md @@ -0,0 +1,12 @@ +# Vision modules + +Re-enabled 2026-07-07 via BrainSim3 compatibility layer in `UKS/`: + +- `Relationship.cs` — `Link` wrapper +- `Thought.BrainSim3Compat.cs` — `SetAttribute`, `GetAttribute`, `Relationships`, etc. +- `UKS.BrainSim3Compat.cs` — `GetOrAddThing`, `SearchForClosestMatch(ref)`, `HasSequence`, etc. +- `BrainSimulator/GlobalUsings.cs` — `Thing` → `Thought` alias + +`ModuleMentalModel*` in this folder are legacy BrainSim3 stubs. The canonical +implementation is `Modules/ModuleMentalModel*` and is excluded here in +`BrainSim Thought.csproj` to avoid duplicate-type build errors. \ No newline at end of file diff --git a/BrainSimulator/Network.cs b/BrainSimulator/Network.cs index 618df39..5381196 100644 --- a/BrainSimulator/Network.cs +++ b/BrainSimulator/Network.cs @@ -112,12 +112,15 @@ public static UdpClient UDPAudioReceive public static void Broadcast(string message) { //Debug.WriteLine("Broadcast: " + message); - if (broadcastAddress is null) SetBroadcastAddress(); + if (broadcastAddress is null && !SetBroadcastAddress()) + return; + if (broadcastAddress is null) + return; byte[] datagram = Encoding.UTF8.GetBytes(message); IPEndPoint ipEnd = new(broadcastAddress, UDPSendPort); UDPBroadcast.SendAsync(datagram, datagram.Length, ipEnd); - } + } public static bool UDP_Send(string message,IPAddress ipToSend, int udpPort) { if (ipToSend is null) return false; @@ -187,7 +190,7 @@ public static bool InitTCP(string podIPString)//This really needs to have some c return true; } - public static void SetBroadcastAddress() + public static bool SetBroadcastAddress() { var host = Dns.GetHostEntry(Dns.GetHostName()); foreach (var ip in host.AddressList) @@ -198,10 +201,11 @@ public static void SetBroadcastAddress() string[] ipComps = ipStr.Split("."); broadcastAddress = IPAddress.Parse(ipComps[0] + "." + ipComps[1] + "." + ipComps[2] + ".255"); - return; + return true; } } - throw new Exception("No network adapters with an IPv4 address in the system!"); + Debug.WriteLine("SetBroadcastAddress: no IPv4 adapter; UDP broadcast disabled."); + return false; } public static void SendStringToPodTCP(string msg) diff --git a/BrainSimulator/Resources/wordlist.txt b/BrainSimulator/Resources/wordlist.txt new file mode 100644 index 0000000..44e875b --- /dev/null +++ b/BrainSimulator/Resources/wordlist.txt @@ -0,0 +1,30 @@ +# Minimal word list for ModuleOnlineInfo.SetupWordList (one word per line). +# Replace or set WORDLIST_PATH to a full dictionary file. +the +be +to +of +and +a +in +that +have +it +for +not +on +with +he +as +you +do +at +dog +cat +bird +fish +horse +tree +water +sun +moon \ No newline at end of file diff --git a/BrainSimulator/UKSContent/NatureAnimals.xml b/BrainSimulator/UKSContent/NatureAnimals.xml new file mode 100644 index 0000000..95fa64f --- /dev/null +++ b/BrainSimulator/UKSContent/NatureAnimals.xml @@ -0,0 +1,1995 @@ + + + + 0 + + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + 5 + + + + 6 + + + + 7 + + + + 8 + + + + 9 + + + + 10 + + + + 11 + + + + 12 + + + + 13 + + + + 14 + + + + 15 + + + + 16 + + + + 17 + + + + 18 + + + + 19 + + + + 20 + + + + 21 + + + + 22 + + + + 23 + + + + 24 + + + + 25 + + + + 26 + + + + 27 + + + + 28 + + + + 29 + + + + 30 + + + + 31 + + + + 32 + + + + 33 + + + + 34 + + + + 35 + + + + 36 + + + + 37 + + + + 38 + + + + 39 + + + + 40 + + + + 41 + + + + 42 + + + + 43 + + + + 44 + + + + 45 + + + + 46 + + + + 47 + + + + 48 + + + + 49 + + + + 50 + + + + 51 + + + + 52 + + + + 53 + + + + 54 + + + + 55 + + + + 56 + + + + 57 + + + + 58 + + + + 59 + + + + 60 + + + + 61 + + + + 62 + + + + 63 + + + + 64 + + + + 65 + + + + 66 + + + + 67 + + + + 68 + + + + 69 + + + + 70 + + + + 71 + + + + 72 + + + + 73 + + + + 74 + + + + 75 + + + + 76 + + + + 77 + + + + 78 + + + + 79 + + + + 80 + + + + 81 + + + + 82 + + + + 83 + + + + 84 + + + + 85 + + + + 86 + + + + 87 + + + + 88 + + + + 89 + + + + 90 + + + + 91 + + + + 92 + + + + 93 + + + + 94 + + + + 95 + + + + 96 + + + + 97 + + + + 98 + + + + 99 + + + + 100 + + + + 101 + + + + 102 + + + + 103 + + + + 104 + + + + 105 + + + + 106 + + 1 + 10 + 0 + + + 107 + + 2 + 10 + 0 + + + 108 + + 3 + 10 + 0 + + + 109 + + 4 + 10 + 0 + + + 110 + + 5 + 10 + 4 + + + 111 + + 6 + 10 + 3 + + + 112 + + 7 + 10 + 3 + + + 113 + + 8 + 10 + 3 + + + 114 + + 9 + 10 + 0 + + + 115 + + 35 + 10 + 0 + + + 116 + + 10 + 10 + 2 + + + 117 + + 11 + 10 + 2 + + + 118 + + 12 + 10 + 2 + + + 119 + + 13 + 10 + 2 + + + 120 + + 14 + 10 + 2 + + + 121 + + 15 + 10 + 2 + + + 122 + + 16 + 10 + 2 + + + 123 + + 16 + 10 + 5 + + + 124 + + 17 + 10 + 2 + + + 125 + + 17 + 10 + 5 + + + 126 + + 18 + 10 + 2 + + + 127 + + 19 + 10 + 2 + + + 128 + + 20 + 10 + 2 + + + 129 + + 21 + 10 + 2 + + + 130 + + 22 + 10 + 11 + + + 131 + + 23 + 10 + 11 + + + 132 + + 24 + 10 + 11 + + + 133 + + 25 + 10 + 11 + + + 134 + + 26 + 10 + 11 + + + 135 + + 27 + 10 + 11 + + + 136 + + 28 + 10 + 4 + + + 137 + + 29 + 10 + 4 + + + 138 + + 30 + 10 + 4 + + + 139 + + 31 + 10 + 4 + + + 140 + + 32 + 10 + 4 + + + 141 + + 33 + 10 + 4 + + + 142 + + 34 + 10 + 33 + + + 143 + + 36 + 10 + 3 + + + 144 + + 39 + 10 + 3 + + + 145 + + 37 + 10 + 36 + + + 146 + + 38 + 10 + 36 + + + 147 + + 87 + 10 + 38 + + + 148 + + 40 + 10 + 37 + + + 149 + + 41 + 10 + 37 + + + 150 + + 42 + 10 + 37 + + + 151 + + 43 + 10 + 37 + + + 152 + + 44 + 10 + 37 + + + 153 + + 45 + 10 + 37 + + + 154 + + 46 + 10 + 40 + + + 155 + + 47 + 10 + 40 + + + 156 + + 48 + 10 + 40 + + + 157 + + 49 + 10 + 40 + + + 158 + + 50 + 10 + 40 + + + 159 + + 51 + 10 + 40 + + + 160 + + 52 + 10 + 40 + + + 161 + + 53 + 10 + 40 + + + 162 + + 54 + 10 + 40 + + + 163 + + 55 + 10 + 41 + + + 164 + + 56 + 10 + 41 + + + 165 + + 57 + 10 + 41 + + + 166 + + 58 + 10 + 41 + + + 167 + + 59 + 10 + 42 + + + 168 + + 60 + 10 + 42 + + + 169 + + 61 + 10 + 42 + + + 170 + + 62 + 10 + 43 + + + 171 + + 63 + 10 + 43 + + + 172 + + 64 + 10 + 43 + + + 173 + + 65 + 10 + 44 + + + 174 + + 66 + 10 + 45 + + + 175 + + 67 + 10 + 45 + + + 176 + + 68 + 10 + 6 + + + 177 + + 69 + 10 + 6 + + + 178 + + 70 + 10 + 6 + + + 179 + + 71 + 10 + 6 + + + 180 + + 72 + 10 + 6 + + + 181 + + 73 + 10 + 6 + + + 182 + + 74 + 10 + 6 + + + 183 + + 75 + 10 + 6 + + + 184 + + 76 + 10 + 6 + + + 185 + + 77 + 10 + 8 + + + 186 + + 78 + 10 + 8 + + + 187 + + 79 + 10 + 8 + + + 188 + + 80 + 10 + 8 + + + 189 + + 81 + 10 + 7 + + + 190 + + 82 + 10 + 7 + + + 191 + + 83 + 10 + 7 + + + 192 + + 84 + 10 + 7 + + + 193 + + 85 + 10 + 7 + + + 194 + + 86 + 10 + 7 + + + 195 + + 40 + 15 + 28 + + + 196 + + 40 + 15 + 30 + + + 197 + + 40 + 15 + 32 + + + 198 + + 41 + 15 + 28 + + + 199 + + 41 + 15 + 31 + + + 200 + + 41 + 15 + 32 + + + 201 + + 42 + 15 + 29 + + + 202 + + 42 + 15 + 31 + + + 203 + + 42 + 15 + 32 + + + 204 + + 43 + 15 + 29 + + + 205 + + 43 + 15 + 31 + + + 206 + + 43 + 15 + 32 + + + 207 + + 44 + 15 + 29 + + + 208 + + 44 + 15 + 31 + + + 209 + + 45 + 15 + 29 + + + 210 + + 37 + 15 + 32 + + + 211 + + 46 + 11 + 69 + + + 212 + + 46 + 22 + 68 + + + 213 + + 46 + 11 + 70 + + + 214 + + 46 + 14 + 77 + + + 215 + + 47 + 11 + 69 + + + 216 + + 47 + 22 + 68 + + + 217 + + 47 + 11 + 70 + + + 218 + + 47 + 14 + 78 + + + 219 + + 48 + 11 + 69 + + + 220 + + 48 + 22 + 68 + + + 221 + + 48 + 14 + 79 + + + 222 + + 49 + 11 + 73 + + + 223 + + 49 + 27 + 68 + + + 224 + + 50 + 11 + 74 + + + 225 + + 50 + 22 + 68 + + + 226 + + 51 + 22 + 68 + + + 227 + + 51 + 11 + 69 + + + 228 + + 52 + 11 + 69 + + + 229 + + 52 + 22 + 68 + + + 230 + + 52 + 11 + 70 + + + 231 + + 53 + 22 + 68 + + + 232 + + 53 + 11 + 69 + + + 233 + + 54 + 11 + 73 + + + 234 + + 54 + 27 + 68 + + + 235 + + 41 + 11 + 74 + + + 236 + + 41 + 11 + 71 + + + 237 + + 41 + 24 + 68 + + + 238 + + 41 + 11 + 76 + + + 239 + + 55 + 24 + 74 + + + 240 + + 56 + 24 + 74 + + + 241 + + 57 + 24 + 74 + + + 242 + + 58 + 24 + 74 + + + 243 + + 42 + 11 + 72 + + + 244 + + 59 + 27 + 68 + + + 245 + + 59 + 11 + 72 + + + 246 + + 60 + 22 + 68 + + + 247 + + 60 + 11 + 72 + + + 248 + + 61 + 22 + 68 + + + 249 + + 61 + 11 + 72 + + + 250 + + 43 + 11 + 73 + + + 251 + + 43 + 11 + 75 + + + 252 + + 43 + 11 + 72 + + + 253 + + 62 + 11 + 73 + + + 254 + + 63 + 11 + 73 + + + 255 + + 63 + 27 + 68 + + + 256 + + 65 + 22 + 68 + + + 257 + + 66 + 11 + 74 + + + 258 + + 66 + 25 + 68 + + + 259 + + 66 + 14 + 80 + + + 260 + + 67 + 25 + 68 + + + 261 + + 46 + 10 + 93 + + + 262 + + 47 + 10 + 93 + + + 263 + + 48 + 10 + 93 + + + 264 + + 52 + 10 + 94 + + + 265 + + 55 + 10 + 94 + + + 266 + + 53 + 10 + 94 + + + 267 + + 46 + 16 + 52 + + + 268 + + 46 + 16 + 47 + + + 269 + + 47 + 16 + 46 + + + 270 + + 52 + 16 + 46 + + + 271 + + 62 + 16 + 64 + + + 272 + + 55 + 16 + 58 + + + 273 + + 46 + 17 + 41 + + + 274 + + 41 + 17 + 43 + + + 275 + + 40 + 17 + 42 + + + 276 + + 49 + 17 + 43 + + + 277 + + 50 + 17 + 41 + + + 278 + + 59 + 17 + 46 + + + 279 + + 55 + 18 + 51 + + + 280 + + 51 + 19 + 55 + + + 281 + + 52 + 18 + 53 + + + 282 + + 53 + 19 + 52 + + + 283 + + 63 + 18 + 62 + + + 284 + + 62 + 19 + 63 + + + 285 + + 58 + 18 + 51 + + + 286 + + 47 + 18 + 51 + + + 287 + + 53 + 20 + 81 + + + 288 + + 55 + 20 + 83 + + + 289 + + 62 + 20 + 86 + + + 290 + + 49 + 20 + 82 + + + 291 + + 63 + 20 + 82 + + + 292 + + 54 + 20 + 82 + + + 293 + + 57 + 20 + 85 + + + 294 + + 56 + 20 + 81 + + + 295 + + 65 + 20 + 86 + + + 296 + + 87 + 20 + 81 + + + 297 + + 46 + 21 + 62 + + + 298 + + 47 + 21 + 51 + + + 299 + + 55 + 21 + 51 + + + 300 + + 52 + 21 + 53 + + + 301 + + 63 + 21 + 62 + + + 302 + + 95 + 10 + 46 + + + 303 + + 95 + 13 + 88 + + + 304 + + 95 + 22 + 68 + + + 305 + + 95 + 20 + 84 + + + 306 + + 96 + 10 + 46 + + + 307 + + 96 + 23 + 68 + + + 308 + + 97 + 10 + 47 + + + 309 + + 97 + 13 + 89 + + + 310 + + 97 + 20 + 84 + + + 311 + + 98 + 10 + 48 + + + 312 + + 98 + 13 + 88 + + + 313 + + 98 + 20 + 84 + + + 314 + + 99 + 10 + 46 + + + 315 + + 99 + 13 + 90 + + + 316 + + 100 + 10 + 55 + + + 317 + + 100 + 20 + 83 + + + 318 + + 101 + 10 + 59 + + + 319 + + 101 + 13 + 91 + + + 320 + + 101 + 20 + 81 + + + 321 + + 102 + 10 + 50 + + + 322 + + 102 + 20 + 81 + + + 323 + + 103 + 10 + 62 + + + 324 + + 103 + 20 + 86 + + + 325 + + 104 + 10 + 57 + + + 326 + + 104 + 20 + 85 + + + 327 + + 105 + 10 + 53 + + + 328 + + 105 + 13 + 88 + + + 329 + + 105 + 20 + 81 + + diff --git a/BrainSimulator/Utils.cs b/BrainSimulator/Utils.cs index 964dcc4..dc57c61 100644 --- a/BrainSimulator/Utils.cs +++ b/BrainSimulator/Utils.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Media.Media3D; +using System.Diagnostics; using System.IO; using System.Reflection.Metadata; @@ -111,7 +112,7 @@ public HSLColor(Dictionary values) saturation = values["Sat+"]; luminance = values["Lum+"]; } - catch { } + catch (Exception ex) { Debug.WriteLine($"HSLColor dictionary parse failed: {ex.Message}"); } } public HSLColor(byte a, byte r, byte g, byte b) { @@ -194,13 +195,12 @@ Color ColorFromHSL2() Color c; - if (000 <= hue && hue < 060) c = Color.FromArgb(255, (byte)((C + m) * 255), (byte)((X + m) * 255), (byte)((0 + m) * 255)); - if (060 <= hue && hue < 120) c = Color.FromArgb(255, (byte)((X + m) * 255), (byte)((C + m) * 255), (byte)((0 + m) * 255)); - if (120 <= hue && hue < 180) c = Color.FromArgb(255, (byte)((0 + m) * 255), (byte)((C + m) * 255), (byte)((X + m) * 255)); - if (180 <= hue && hue < 240) c = Color.FromArgb(255, (byte)((0 + m) * 255), (byte)((X + m) * 255), (byte)((C + m) * 255)); - if (240 <= hue && hue < 300) c = Color.FromArgb(255, (byte)((X + m) * 255), (byte)((0 + m) * 255), (byte)((C + m) * 255)); - if (300 <= hue && hue < 345) c = Color.FromArgb(255, (byte)((C + m) * 255), (byte)((0 + m) * 255), (byte)((C + m) * 255)); - if (hue > 345) c = Color.FromArgb(255, (byte)((C + m) * 255), (byte)((X + m) * 255), (byte)((0 + m) * 255)); + if (hue < 60) c = Color.FromArgb(255, (byte)((C + m) * 255), (byte)((X + m) * 255), (byte)((0 + m) * 255)); + else if (hue < 120) c = Color.FromArgb(255, (byte)((X + m) * 255), (byte)((C + m) * 255), (byte)((0 + m) * 255)); + else if (hue < 180) c = Color.FromArgb(255, (byte)((0 + m) * 255), (byte)((C + m) * 255), (byte)((X + m) * 255)); + else if (hue < 240) c = Color.FromArgb(255, (byte)((0 + m) * 255), (byte)((X + m) * 255), (byte)((C + m) * 255)); + else if (hue < 300) c = Color.FromArgb(255, (byte)((X + m) * 255), (byte)((0 + m) * 255), (byte)((C + m) * 255)); + else c = Color.FromArgb(255, (byte)((C + m) * 255), (byte)((0 + m) * 255), (byte)((X + m) * 255)); return c; } @@ -228,9 +228,8 @@ public bool Equals(HSLColor c1) if (c1.luminance > .95) return true; } float absHueDiff = Abs(hue - c1.hue); - if (absHueDiff < 5 || absHueDiff > 355) - return true; - return false; + float circularHueDiff = Min(absHueDiff, 360f - absHueDiff); + return circularHueDiff < 5; } } @@ -272,7 +271,7 @@ public static Control FindByName(Visual v, string name) if (c2 is not null) return c2; } - catch { } + catch (Exception ex) { Debug.WriteLine($"FindByName walk failed for '{name}': {ex.Message}"); } } } return null; diff --git a/BrainSimulator/archive/legacy/MainWindowPythonModules.cs b/BrainSimulator/archive/legacy/MainWindowPythonModules.cs new file mode 100644 index 0000000..7462c7f --- /dev/null +++ b/BrainSimulator/archive/legacy/MainWindowPythonModules.cs @@ -0,0 +1,173 @@ +/* + * Brain Simulator Through + * + * Copyright (c) 2026 Charles Simon + * + * This file is part of Brain Simulator Through and is licensed under + * the MIT License. You may use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of this software under the terms of + * the MIT License. + * + * See the LICENSE file in the project root for full license information. + */ + +using Python.Runtime; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows; +using UKS; +using System.Windows.Interop; + + +namespace BrainSimulator; + +public partial class MainWindow : Window +{ + public static List<(string, dynamic)> activePythonModules = new(); + public static List GetPythonModules() + { + //this is a buffer of python modules so they can be imported once and run many times. + List<(string, dynamic)> modules = new List<(string, dynamic)>(); + List pythonFiles = new(); + try + { + pythonFiles = Directory.GetFiles(@".\pythonModules", "*.py").ToList(); + string destDir = Directory.GetCurrentDirectory(); + string sourceDir = destDir + "\\PythonModules"; + for (int i = 0; i < pythonFiles.Count; i++) + { + if (pythonFiles[i].StartsWith("utils")) continue; + pythonFiles[i] = Path.GetFileName(pythonFiles[i]); + } + } + catch + { + + } + return pythonFiles; + } + static void RunScript(string moduleLabel) + { + bool firstTime = false; + //get the ModuleType + Thing tModule = theUKS.Labeled(moduleLabel); + if (tModule == null) { return; } + Thing tModuleType = tModule.Parents.FindFirst(x => x.HasAncestorLabeled("AvailableModule")); + if (tModuleType == null) return; + string moduleType = tModuleType.Label; + moduleType = moduleType.Replace(".py", ""); + + //if this is the very first call, initialize the python engine + if (Runtime.PythonDLL == null) + { + try + { + //TODO use variable + Runtime.PythonDLL = @"Python310"; + + PythonEngine.Initialize(); + dynamic sys = Py.Import("sys"); + dynamic os = Py.Import("os"); + sys.path.append(os.getcwd()); // enables finding scriptName module + sys.path.append(os.getcwd() + "\\pythonModules"); + } + catch + { + Console.WriteLine("Python engine initialization failed"); + return; + } + } + using (Py.GIL()) + { + var theModuleEntry = activePythonModules.FirstOrDefault(x => x.Item1.ToLower() == moduleLabel.ToLower()); + if (string.IsNullOrEmpty(theModuleEntry.Item1)) + { + //if this is the first time this modulw has been used + try + { + Console.WriteLine("Loading " + moduleLabel); + dynamic theModule = Py.Import(moduleType); + theModuleEntry = (moduleLabel, theModule); + activePythonModules.Add(theModuleEntry); + theModule.Init(); + firstTime = true; + } + catch (Exception ex) + { + Console.WriteLine("Load/initialize failed for module: " + moduleLabel + " Reason: " + ex.Message); + theModuleEntry = (moduleLabel, null); + activePythonModules.Add(theModuleEntry); + } + } + if (theModuleEntry.Item2 != null) + { + try + { + if (!theModuleEntry.Item2.Fire()) + { } //this doesn't seem to work because it thows an exception instead + if (firstTime) + { + var HWND = theModuleEntry.Item2.GetHWND(); + var ss = HWND.ToString(); + // this works, and returns 1322173 + int intValue = Convert.ToInt32(ss, 16); + firstTime = false; + SetOwner(intValue); + } + } + catch (Exception ex) + { + activePythonModules.Remove(theModuleEntry); + MainWindow.theWindow.DeactivateModule(moduleLabel); + Console.WriteLine("Fire method call failed for module: " + moduleLabel + " Reason: " + ex.Message); + } + } + } + } + + + private const int GWL_HWNDPARENT = -8; + + [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")] + private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + + [DllImport("user32.dll", SetLastError = true)] + private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + + static void SetOwner(int HWND) + { + // Example usage + IntPtr childHwnd = new IntPtr(HWND); // Child window handle + //IntPtr ownerHwnd = new IntPtr(654321); // New owner window handle + Window window = Window.GetWindow(MainWindow.theWindow); + var wih = new WindowInteropHelper(window); + IntPtr ownerHwnd = wih.Handle; + + ChangeWindowOwner(childHwnd, ownerHwnd); + } + + static void ChangeWindowOwner(IntPtr childHwnd, IntPtr ownerHwnd) + { + IntPtr result = 0; + if (IntPtr.Size == 8) + SetWindowLongPtr(childHwnd, GWL_HWNDPARENT, ownerHwnd); + else + SetWindowLong(childHwnd, GWL_HWNDPARENT, ownerHwnd); + + if (result == IntPtr.Zero) + { + int errorCode = Marshal.GetLastWin32Error(); + Console.WriteLine($"Failed to change window owner. Error code: {errorCode}"); + } + else + { + Console.WriteLine("Window owner changed successfully."); + } + } +} + + + diff --git a/BrainSimulator/archive/legacy/README.md b/BrainSimulator/archive/legacy/README.md new file mode 100644 index 0000000..746dae3 --- /dev/null +++ b/BrainSimulator/archive/legacy/README.md @@ -0,0 +1,10 @@ +# Legacy BrainSim3 sources (not compiled) + +Moved from `BrainSimulator/` root on 2026-07-07. Excluded via `` in `BrainSim Thought.csproj`. + +| File | Reason | +|------|--------| +| `XmlFile.cs` | BrainSim3Data XML serializer; app uses `UKS.LoadUKSfromXMLFile` / `SaveUKStoXMLFile` | +| `MainWindowPythonModules.cs` | Duplicate Python glue with hardcoded `Python310`; superseded by `ModuleHandler.cs` | + +Do not re-enable without porting to current UKS persistence and `PythonPath` configuration. \ No newline at end of file diff --git a/BrainSimulator/XmlFile.cs b/BrainSimulator/archive/legacy/XmlFile.cs similarity index 100% rename from BrainSimulator/XmlFile.cs rename to BrainSimulator/archive/legacy/XmlFile.cs diff --git a/PythonProj/MainWindow.py b/PythonProj/MainWindow.py index f5422e5..cb31961 100644 --- a/PythonProj/MainWindow.py +++ b/PythonProj/MainWindow.py @@ -16,6 +16,7 @@ def __init__(self, level: Union[tk.Tk, tk.Toplevel]) -> None: title: str = titleBase super(MainWindow, self).__init__( title=title, level=level, module_type=os.path.basename(__file__)) + self._closed = False self.setupUKS() self.build() @@ -36,9 +37,6 @@ def build(self): self.saveAsButton.pack(side='right',padx=50) self.saveButton.pack(side='top',pady=20) self.setupcontent() - - if sys.argv[0] != "": - self.level.mainloop() def setupcontent(self): self.moduleList.delete(0,'end') @@ -147,12 +145,21 @@ def activateModule(self,moduleTypeLabel): def onClosing(self): - print ("MainWindow closing") - os._exit(0) + print("MainWindow closing") + self._closed = True + try: + self.level.destroy() + except tk.TclError: + pass def fire(self): - #Put your functional code HERE - #This function is called repeateldly so you may wish to do things only on a timer like this: + if self._closed: + return False + try: + if not self.level.winfo_exists(): + return False + except tk.TclError: + return False curr_time: float = time.time() try: if curr_time > (self.prev_time + TIMEDELAY): @@ -163,9 +170,11 @@ def fire(self): #you always nee this: self.setupcontent() self.level.update() - #don't ever close this module while the program is running return True + def close(self): + self.onClosing() + ###################### ## Expose Methods ## @@ -185,6 +194,7 @@ def GetHWND() -> int: def SetLabel(label): view.setLabel(label) -if sys.argv[0] != "": +if __name__ == "__main__": Init() + view.level.mainloop() diff --git a/PythonProj/module_uks_tree.py b/PythonProj/module_uks_tree.py index 17232c1..3400e8c 100644 --- a/PythonProj/module_uks_tree.py +++ b/PythonProj/module_uks_tree.py @@ -138,11 +138,6 @@ def build(self) -> None: self.tree_view.bind("<>", self.handle_close_event) self.tree_view.bind("", self.handle_mouse_enter) self.tree_view.bind("", self.handle_mouse_leave) - - if sys.argv[0] != "": - self.level.mainloop() - - ############ ## Fire ## @@ -183,5 +178,6 @@ def SetLabel(label): def Close(): view.close() -if sys.argv[0] != "": +if __name__ == "__main__": Init() + view.level.mainloop() diff --git a/PythonProj/utils.py b/PythonProj/utils.py index e2e6887..fd26b31 100644 --- a/PythonProj/utils.py +++ b/PythonProj/utils.py @@ -33,26 +33,23 @@ def __init__(self, self.window_height = None self.window_x = None self.window_y = None - #BUG...if you enable the following line, the WINDOWS program will crash if you move/resize a window - #self.level.bind("",self.resize) + # Call bind_resize_events() after build if geometry sync to UKS is needed. def setLabel(self, new_label: str): self.label = new_label - def resize(self,event): - #breakpoint() - #this actually receives ALL the configuration events...so we have to sort out resize/move and the event source - if hasattr(event.widget, "widgetName"): - pass - else: #if it doesn't have a name, it must be top level - if (self.window_width != event.width) or (self.window_height != event.height): - #if height/width changed, it must be resize - self.window_width, self.window_height = event.width,event.height - if (self.window_x != event.x ) or (self.window_y != event.y): - self.window_x, self.window_y = event.x,event.y - print(self.module_type, self.label) - print(self.level.winfo_geometry()) - #TODO Add code to update values in UKS + def bind_resize_events(self): + self.level.bind("", self.resize) + + def resize(self, event): + # Only handle the toplevel window; child Configure events caused Windows crashes. + if event.widget is not self.level: + return + if (self.window_width != event.width) or (self.window_height != event.height): + self.window_width, self.window_height = event.width, event.height + if (self.window_x != event.x) or (self.window_y != event.y): + self.window_x, self.window_y = event.x, event.y + # TODO: persist geometry to UKS module attributes when safe def close(self): self.level.destroy() diff --git a/Tests/ModuleAlgorithm.Tests.cs b/Tests/ModuleAlgorithm.Tests.cs index 66a1073..4b55979 100644 --- a/Tests/ModuleAlgorithm.Tests.cs +++ b/Tests/ModuleAlgorithm.Tests.cs @@ -37,7 +37,7 @@ public ModuleAlgorithmTests() // Load algorithm.xml from UKSContent folder - string xmlPath = Path.Combine(brainSimRoot,"brainsimulator", "UKSContent", "algorithm.xml"); + string xmlPath = Path.Combine(brainSimRoot, "BrainSimulator", "UKSContent", "algorithm.xml"); if (!File.Exists(xmlPath)) { // Try alternative path diff --git a/Tests/NatureAnimalsXmlTests.cs b/Tests/NatureAnimalsXmlTests.cs new file mode 100644 index 0000000..3954922 --- /dev/null +++ b/Tests/NatureAnimalsXmlTests.cs @@ -0,0 +1,87 @@ +/* + * Brain Simulator Thought — NatureAnimals.xml import verification + */ + +using System.IO; +using UKS; +using Xunit; + +namespace UKS.Tests; + +public class NatureAnimalsXmlTests +{ + private static string XmlPath() + { + string fromTest = Path.Combine( + Directory.GetCurrentDirectory(), + "..", "..", "..", "..", + "BrainSimulator", "UKSContent", "NatureAnimals.xml"); + if (File.Exists(fromTest)) return Path.GetFullPath(fromTest); + + string fromRepo = Path.Combine( + AppContext.BaseDirectory, + "..", "..", "..", "..", "..", + "BrainSimulator", "UKSContent", "NatureAnimals.xml"); + return Path.GetFullPath(fromRepo); + } + + private static UKS LoadNatureUks() + { + var uks = new UKS(clear: true); + string path = XmlPath(); + Assert.True(File.Exists(path), $"NatureAnimals.xml not found at {path}"); + Assert.True(uks.LoadUKSfromXMLFile(path)); + return uks; + } + + private static void AssertLink(UKS uks, string from, string linkType, string to) + { + Thought f = uks.Labeled(from); + Thought lt = uks.Labeled(linkType); + Thought t = uks.Labeled(to); + Assert.NotNull(f); + Assert.NotNull(lt); + Assert.NotNull(t); + Assert.NotNull(f.HasLink(lt, t)); + } + + [Fact] + public void NatureAnimals_LoadsFromXml() + { + var uks = LoadNatureUks(); + Assert.NotNull(uks.Labeled("Fido")); + Assert.NotNull(uks.Labeled("mammal")); + Assert.True(uks.AtomicThoughts.Count > 100); + } + + [Fact] + public void NatureAnimals_FidoPattern() + { + var uks = LoadNatureUks(); + AssertLink(uks, "Fido", "is-a", "dog"); + AssertLink(uks, "Fido", "is", "brown"); + AssertLink(uks, "Fido", "has.4", "leg"); + AssertLink(uks, "dog", "has.4", "leg"); + } + + [Fact] + public void NatureAnimals_TripperException() + { + var uks = LoadNatureUks(); + AssertLink(uks, "Tripper", "is-a", "dog"); + AssertLink(uks, "Tripper", "has.3", "leg"); + } + + [Fact] + public void NatureAnimals_TaxonomyAndCrossSpecies() + { + var uks = LoadNatureUks(); + AssertLink(uks, "dog", "is-a", "mammal"); + AssertLink(uks, "eagle", "is-a", "bird"); + AssertLink(uks, "salmon", "is-a", "fish"); + AssertLink(uks, "dog", "isSimilarTo", "wolf"); + AssertLink(uks, "eagle", "predatorOf", "mouse"); + AssertLink(uks, "whale", "differsFrom", "fish"); + AssertLink(uks, "penguin", "livesIn", "antarctica"); + } +} \ No newline at end of file diff --git a/Tests/NonUksP0FixesRegressionTests.cs b/Tests/NonUksP0FixesRegressionTests.cs new file mode 100644 index 0000000..ee52fe2 --- /dev/null +++ b/Tests/NonUksP0FixesRegressionTests.cs @@ -0,0 +1,38 @@ +/* + * Regression tests for P0 non-UKS fixes (2026-07-07). + */ + +using BrainSimulator; +using Xunit; + +namespace UKS.Tests; + +public class NonUksP0FixesRegressionTests +{ + [Fact] + public void HSLColor_Equals_does_not_merge_hues_beyond_threshold_across_wrap() + { + var a = new HSLColor(350f, 0.8f, 0.5f); + var b = new HSLColor(10f, 0.8f, 0.5f); + + Assert.False(a.Equals(b)); + } + + [Fact] + public void HSLColor_Equals_distinguishes_visually_different_hues() + { + var a = new HSLColor(30f, 0.8f, 0.5f); + var b = new HSLColor(200f, 0.8f, 0.5f); + + Assert.False(a.Equals(b)); + } + + [Fact] + public void HSLColor_Equals_treats_near_wrap_pairs_as_equal() + { + var a = new HSLColor(2f, 0.8f, 0.5f); + var b = new HSLColor(358f, 0.8f, 0.5f); + + Assert.True(a.Equals(b)); + } +} \ No newline at end of file diff --git a/Tests/NonUksP1FixesRegressionTests.cs b/Tests/NonUksP1FixesRegressionTests.cs new file mode 100644 index 0000000..4d559b7 --- /dev/null +++ b/Tests/NonUksP1FixesRegressionTests.cs @@ -0,0 +1,39 @@ +/* + * Regression tests for P1 non-UKS fixes (2026-07-07). + */ + +using BrainSimulator; +using UKS; +using Xunit; + +namespace UKS.Tests; + +public class NonUksP1FixesRegressionTests +{ + [Fact] + public void Broadcast_does_not_throw_when_ipv4_unavailable() + { + Exception ex = Record.Exception(() => Network.Broadcast("brainsim-test")); + Assert.Null(ex); + } + + [Fact] + public void DeactivateModule_preserves_shared_link_targets() + { + var uks = new UKS.UKS(clear: true); + uks.CreateInitialStructure(); + var handler = new ModuleHandler(); + + Thought shared = uks.GetOrAddThought("SharedConfig", "Object"); + Thought moduleType = uks.GetOrAddThought("ModuleTest", uks.Labeled("AvailableModule")); + Thought instance = uks.CreateInstanceOf(moduleType); + uks.AddStatement(instance, "is", shared); + + string instanceLabel = instance.Label; + handler.DeactivateModule(instanceLabel); + + Assert.NotNull(uks.Labeled("SharedConfig")); + Assert.NotNull(uks.Labeled(moduleType.Label)); + Assert.Null(uks.Labeled(instanceLabel)); + } +} \ No newline at end of file diff --git a/Tests/NonUksP2FixesRegressionTests.cs b/Tests/NonUksP2FixesRegressionTests.cs new file mode 100644 index 0000000..9771bcf --- /dev/null +++ b/Tests/NonUksP2FixesRegressionTests.cs @@ -0,0 +1,52 @@ +/* + * Regression tests for P2 non-UKS fixes (2026-07-07). + */ + +using UKS; +using Xunit; + +namespace UKS.Tests; + +public class NonUksP2FixesRegressionTests +{ + [Fact] + public void Thought_GetTargetOfFirstLinkOfType_string_overload() + { + var uks = new UKS.UKS(clear: true); + uks.CreateInitialStructure(); + Thought parent = uks.GetOrAddThought("phrase1", "Object"); + Thought linkType = uks.GetOrAddThought("soundAs", "LinkType"); + var seq = uks.AddSequenceAndLink(parent, linkType, new List { uks.GetOrAddThought("note1", "Object") }); + Assert.NotNull(seq); + Thought target = parent.GetTargetOfFirstLinkOfType("soundAs"); + Assert.Same(seq, target); + } + + [Fact] + public void Bundled_wordlist_resource_exists() + { + string repoRoot = FindRepoRoot(); + string wordlist = Path.Combine(repoRoot, "BrainSimulator", "Resources", "wordlist.txt"); + Assert.True(File.Exists(wordlist)); + } + + [Fact] + public void Legacy_sources_moved_to_archive() + { + string repoRoot = FindRepoRoot(); + Assert.True(File.Exists(Path.Combine(repoRoot, "BrainSimulator", "archive", "legacy", "XmlFile.cs"))); + Assert.False(File.Exists(Path.Combine(repoRoot, "BrainSimulator", "XmlFile.cs"))); + } + + private static string FindRepoRoot() + { + var dir = new DirectoryInfo(Directory.GetCurrentDirectory()); + while (dir is not null) + { + if (File.Exists(Path.Combine(dir.FullName, "BrainSim Thought.sln"))) + return dir.FullName; + dir = dir.Parent; + } + throw new DirectoryNotFoundException("BrainSim Thought repo root not found."); + } +} \ No newline at end of file diff --git a/Tests/NonUksRemainingFixesRegressionTests.cs b/Tests/NonUksRemainingFixesRegressionTests.cs new file mode 100644 index 0000000..5f52939 --- /dev/null +++ b/Tests/NonUksRemainingFixesRegressionTests.cs @@ -0,0 +1,56 @@ +/* + * Regression tests for remaining P1 (#9-10) and P3 (#19-23) non-UKS fixes (2026-07-07). + */ + +using System.Reflection; +using System.Threading.Tasks; +using BrainSimulator; +using BrainSimulator.Modules; +using Xunit; + +namespace UKS.Tests; + +public class NonUksRemainingFixesRegressionTests +{ + [Fact] + public void GetStringFromThoughtLabel_empty_returns_empty() + { + var method = typeof(ModuleGPTInfo).GetMethod( + "GetStringFromThoughtLabel", + BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(method); + var result = (string)method!.Invoke(null, new object[] { "" })!; + Assert.Equal("", result); + } + + [Fact] + public void HSLColor_ToColor_hue345_returns_defined_color() + { + var hsl = new HSLColor(345f, 0.8f, 0.5f); + var color = hsl.ToColor(); + Assert.InRange(color.R + color.G + color.B, 1, 765); + } + + [Theory] + [InlineData(nameof(ModuleOnlineInfo.GetKidsWordsmythNet))] + [InlineData(nameof(ModuleOnlineInfo.GetWiktionaryData))] + [InlineData(nameof(ModuleOnlineInfo.GetChatGPTResult))] + [InlineData(nameof(ModuleOnlineInfo.GetWikidataData))] + [InlineData(nameof(ModuleOnlineInfo.GetFreeDictionaryAPIData))] + [InlineData(nameof(ModuleOnlineInfo.GetWebstersDictionaryAPIData))] + public void ModuleOnlineInfo_public_fetch_methods_return_task(string methodName) + { + var method = typeof(ModuleOnlineInfo).GetMethod(methodName); + Assert.NotNull(method); + Assert.Equal(typeof(Task), method!.ReturnType); + } + + [Fact] + public void ModuleBalanceTreeDlg_code_behind_uses_xaml_cs_suffix() + { + var repoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..")); + var path = Path.Combine(repoRoot, "BrainSimulator", "Modules", "Agents", "ModuleBalanceTreeDlg.xaml.cs"); + Assert.True(File.Exists(path), $"Expected code-behind at {path}"); + Assert.False(File.Exists(path.Replace(".xaml.cs", ".xml.cs"))); + } +} \ No newline at end of file diff --git a/Tests/UKSFixesRegressionTests.cs b/Tests/UKSFixesRegressionTests.cs new file mode 100644 index 0000000..f0ef3bd --- /dev/null +++ b/Tests/UKSFixesRegressionTests.cs @@ -0,0 +1,117 @@ +/* + * Regression tests for UKS fixes (2026-07-07 inspection) + */ + +using System.IO; +using UKS; +using Xunit; + +namespace UKS.Tests; + +public class UKSFixesRegressionTests +{ + private static UKS CreateUks() { var uks = new UKS(clear: true); uks.CreateInitialStructure(); return uks; } + + [Fact] + public void LinksAreExclusive_CommonParentSources_AreExclusive() + { + var uks = CreateUks(); + uks.AddStatement("animal", "is-a", "Object"); + uks.AddStatement("dog", "is-a", "animal"); + uks.AddStatement("cat", "is-a", "animal"); + uks.AddStatement("legs", "is-a", "Object"); + uks.AddStatement("has.4", "is-a", "has"); + + var dogLegs = uks.GetLink(uks.AddStatement("dog", "has.4", "legs")); + var catLegs = uks.GetLink(uks.AddStatement("cat", "has.4", "legs")); + + Assert.NotNull(dogLegs); + Assert.NotNull(catLegs); + Assert.True(uks.LinksAreExclusive_ForTests(dogLegs, catLegs)); + } + + [Fact] + public void ChildrenWithSubclasses_UsesIsA_NotLabelPrefix() + { + var uks = CreateUks(); + uks.AddStatement("dog", "is-a", "Object"); + uks.AddStatement("doghouse", "is-a", "Object"); + uks.AddStatement("puppy", "is-a", "dog"); + + var dog = uks.Labeled("dog"); + var doghouse = uks.Labeled("doghouse"); + var puppy = uks.Labeled("puppy"); + var children = dog.ChildrenWithSubclasses; + + Assert.Contains(puppy, children); + Assert.DoesNotContain(doghouse, children); + } + + [Fact] + public void AddStatement_WiresLabeledPlaceholder() + { + var uks = CreateUks(); + uks.AddStatement("dog", "is-a", "Object"); + uks.AddStatement("Fido", "is-a", "dog"); + + var placeholder = new Link { Label = "mylink" }; + uks.AtomicThoughts.Add(placeholder); + + var link = uks.AddStatement(uks.Labeled("Fido"), uks.Labeled("is-a"), uks.Labeled("dog"), "mylink"); + + Assert.NotNull(link); + Assert.Same(uks.Labeled("Fido"), link.From); + Assert.NotNull(link.From.HasLink(uks.Labeled("is-a"), uks.Labeled("dog"))); + } + + [Fact] + public void XmlRoundTrip_PreservesLink() + { + var uks = CreateUks(); + uks.AddStatement("dog", "is-a", "Object"); + uks.AddStatement("Fido", "is-a", "dog"); + + string path = Path.Combine(Path.GetTempPath(), $"uks_roundtrip_{Guid.NewGuid():N}.xml"); + try + { + Assert.True(uks.SaveUKStoXMLFile(path)); + + var uks2 = new UKS(clear: true); + Assert.True(uks2.LoadUKSfromXMLFile(path)); + + Assert.NotNull(uks2.Labeled("Fido")); + Assert.NotNull(uks2.GetLink(uks2.Labeled("Fido"), uks2.Labeled("is-a"), uks2.Labeled("dog"))); + } + finally + { + if (File.Exists(path)) File.Delete(path); + } + } + + [Fact] + public void ExportTextFile_ThrowsOnInvalidPath() + { + var uks = CreateUks(); + uks.AddStatement("dog", "is-a", "Object"); + Assert.Throws(() => + uks.ExportTextFile("Object", "/nonexistent_path_zzzz/export.txt")); + } + + [Fact] + public void HasSequence_UnsupportedFlagsThrow() + { + var uks = CreateUks(); + var targets = new List { uks.GetOrAddThought("A"), uks.GetOrAddThought("B") }; + Assert.Throws(() => + uks.HasSequence(targets, uks.Labeled("spelled"), circularSearch: true)); + } + + [Fact] + public void CreateMinimumStructure_RegistersNxtNotNxe() + { + var uks = new UKS(clear: true); + uks.CreateMinimumStructureForTests(); + Assert.NotNull(uks.Labeled("NXT")); + Assert.Null(uks.Labeled("NXE")); + } +} \ No newline at end of file diff --git a/Tests/VisionCompatRegressionTests.cs b/Tests/VisionCompatRegressionTests.cs new file mode 100644 index 0000000..33e8f63 --- /dev/null +++ b/Tests/VisionCompatRegressionTests.cs @@ -0,0 +1,52 @@ +/* + * Regression tests for Vision / BrainSim3 UKS compatibility layer. + */ + +using UKS; +using Xunit; + +namespace UKS.Tests; + +public class VisionCompatRegressionTests +{ + [Fact] + public void GetOrAddThing_alias_matches_GetOrAddThought() + { + var uks = new UKS.UKS(clear: true); + uks.CreateInitialStructure(); + Thought t = uks.GetOrAddThing("VisionTest", "Object"); + Assert.Same(t, uks.Labeled("VisionTest")); + } + + [Fact] + public void SetAttribute_and_GetAttribute_round_trip() + { + var uks = new UKS.UKS(clear: true); + uks.CreateInitialStructure(); + uks.AddStatement("Color", "is-a", "Attribute"); + Thought shape = uks.GetOrAddThought("shape1", "Object"); + Thought color = uks.GetOrAddThought("color1", "Color"); + shape.SetAttribute(color); + Assert.Same(color, shape.GetAttribute("Color")); + } + + [Fact] + public void SearchForClosestMatch_ref_returns_ranked_results() + { + var uks = new UKS.UKS(clear: true); + uks.CreateInitialStructure(); + Thought root = uks.GetOrAddThought("Stored", "Object"); + Thought a = uks.GetOrAddThought("a", root); + Thought b = uks.GetOrAddThought("b", root); + Thought target = uks.GetOrAddThought("target", "Object"); + uks.AddStatement(target, "is", a); + uks.AddStatement(a, "is", b); + + float best = 0; + Thought? first = uks.SearchForClosestMatch(target, root, ref best); + Assert.NotNull(first); + float best2 = 0; + Thought? second = uks.GetNextClosestMatch(ref best2); + Assert.True(first is not null); + } +} \ No newline at end of file diff --git a/UKS/IReadOnlyListExtensiion.cs b/UKS/IReadOnlyListExtension.cs similarity index 100% rename from UKS/IReadOnlyListExtensiion.cs rename to UKS/IReadOnlyListExtension.cs diff --git a/UKS/Relationship.cs b/UKS/Relationship.cs new file mode 100644 index 0000000..0a21110 --- /dev/null +++ b/UKS/Relationship.cs @@ -0,0 +1,33 @@ +/* + * BrainSim3 compatibility wrapper over UKS Link (Thought/Link model). + */ + +namespace UKS; + +/// Legacy BrainSim3 relationship view of a . +public sealed class Relationship +{ + public Link Link { get; } + + public Relationship(Link link) => Link = link; + + public Thought? target => Link?.To; + public Thought? relType => Link?.LinkType; + public Thought? reltype => Link?.LinkType; + + public float Weight + { + get => Link?.Weight ?? 0f; + set { if (Link is not null) Link.Weight = value; } + } + + public TimeSpan TimeToLive + { + get => Link?.TimeToLive ?? TimeSpan.MaxValue; + set { if (Link is not null) Link.TimeToLive = value; } + } + + public static Relationship? FromLink(Link? link) => link is null ? null : new Relationship(link); + + public static implicit operator Relationship?(Link? link) => FromLink(link); +} \ No newline at end of file diff --git a/UKS/Thought.BrainSim3Compat.cs b/UKS/Thought.BrainSim3Compat.cs new file mode 100644 index 0000000..8365bcb --- /dev/null +++ b/UKS/Thought.BrainSim3Compat.cs @@ -0,0 +1,104 @@ +/* + * BrainSim3 / Vision API compatibility on Thought (maps to Link graph). + */ + +namespace UKS; + +public partial class Thought +{ + /// Legacy casing alias for . + public DateTime lastFiredTime + { + get => LastFiredTime; + set => LastFiredTime = value; + } + + public IReadOnlyList Relationships => + LinksTo.Select(r => Relationship.FromLink(r)).Where(r => r is not null).Cast().ToList(); + + public IReadOnlyList RelationshipsFrom => + LinksFrom.Select(r => Relationship.FromLink(r)).Where(r => r is not null).Cast().ToList(); + + public void SetFired() => Fire(); + + public bool HasAncestorLabeled(string label) + { + Thought? ancestor = UKS.theUKS?.Labeled(label); + return ancestor is not null && HasAncestor(ancestor); + } + + public Thought? GetAttribute(string name) + { + if (string.IsNullOrWhiteSpace(name)) return null; + foreach (Link r in LinksTo) + { + if (r.LinkType?.Label is not ("hasAttribute" or "is")) continue; + if (r.To is null) continue; + if (string.Equals(r.To.Label, name, StringComparison.OrdinalIgnoreCase)) + return r.To; + foreach (Thought parent in r.To.Parents) + { + if (string.Equals(parent.Label, name, StringComparison.OrdinalIgnoreCase)) + return r.To; + } + if (r.To.HasAncestorLabeled(name)) + return r.To; + } + return null; + } + + public Relationship? SetAttribute(Thought attribute) + { + if (attribute is null || UKS.theUKS is null) return null; + Thought hasAttribute = UKS.theUKS.GetOrAddThought("hasAttribute", "LinkType"); + foreach (Link existing in LinksTo.ToList()) + { + if (existing.LinkType?.Label != "hasAttribute") continue; + if (existing.To is null) continue; + bool sameCategory = existing.To.Parents.Any(p => attribute.Parents.Contains(p)) + || existing.To.Label == attribute.Label; + if (sameCategory) + RemoveLink(existing); + } + Link link = UKS.theUKS.AddStatement(this, hasAttribute, attribute); + return Relationship.FromLink(link); + } + + public void AddRelationship(Thought target, string linkTypeLabel) + { + if (target is null || UKS.theUKS is null) return; + Thought linkType = UKS.theUKS.GetOrAddThought(linkTypeLabel, "LinkType"); + UKS.theUKS.AddStatement(this, linkType, target); + } + + public void AddRelationship(string targetLabel, string linkTypeLabel) + { + if (UKS.theUKS is null) return; + Thought target = UKS.theUKS.GetOrAddThought(targetLabel, "Object"); + AddRelationship(target, linkTypeLabel); + } + + public List GetRelationshipsWithAncestor(Thought ancestor) + { + List retVal = new(); + if (ancestor is null) return retVal; + foreach (Link r in LinksTo) + if (r.To?.HasAncestor(ancestor) == true) + { + var wrapped = Relationship.FromLink(r); + if (wrapped is not null) retVal.Add(wrapped); + } + return retVal; + } + + public Relationship? HasRelationshipWithAncestorLabeled(string label) + { + Thought? ancestor = UKS.theUKS?.Labeled(label); + if (ancestor is null) return null; + Link? link = LinksTo.FindFirst(r => r.To?.HasAncestor(ancestor) == true); + return Relationship.FromLink(link); + } + + public void AddRelationship(Thought target, Thought linkType) => + UKS.theUKS?.AddStatement(this, linkType, target); +} \ No newline at end of file diff --git a/UKS/Thought.cs b/UKS/Thought.cs index 17f4e86..43d87e1 100644 --- a/UKS/Thought.cs +++ b/UKS/Thought.cs @@ -58,9 +58,10 @@ public override string ToString() /// A Thought is an atomic unit of thought. In the lexicon of graphs, a Thought is both a "node" and an Edge. /// A Thought can represent anything, physical object, attribute, word, action, feeling, etc. /// -public class Thought +public partial class Thought { - private static Queue recentlyFired = new(); + private static readonly Queue recentlyFired = new(); + private static readonly object recentlyFiredLock = new(); public static Thought IsA { get => ThoughtLabels.GetThought("is-a"); } //this is a cache value shortcut for (Thought)"is-a" private readonly List _linksTo = new(); // links to "has", "is", is-a, many others @@ -103,7 +104,7 @@ public string Label public void Delete() { - if (LinksFrom.FindFirst(x=>x.LinkType.Label == "VLU") is not null) + if (LinksFrom.FindFirst(x => x.LinkType?.Label == "VLU") is not null) { //this Thought is the object of a sequence. } @@ -274,15 +275,16 @@ public IReadOnlyList ChildrenWithSubclasses { get { - List retVal = Children.ToList(); - for (int i = 0; i < retVal.Count; i++) + List retVal = new(); + HashSet seen = new(); + foreach (Thought child in Children) { - Thought t = retVal[i]; - if (t.Label.StartsWith(this._label)) + if (seen.Add(child)) + retVal.Add(child); + foreach (Thought descendant in child.Descendants) { - retVal.AddRange(t.Children); - retVal.RemoveAt(i); - i--; + if (seen.Add(descendant)) + retVal.Add(descendant); } } return retVal; @@ -376,21 +378,30 @@ public void Fire() private void AddToRecentlyFired() { int maxCount = 100; - recentlyFired.Enqueue(this); - while (recentlyFired.Count > maxCount) _ = recentlyFired.Dequeue(); + lock (recentlyFiredLock) + { + recentlyFired.Enqueue(this); + while (recentlyFired.Count > maxCount) _ = recentlyFired.Dequeue(); + } } public static void DeleteFromRecentlyFired(Thought t) { - //CAUTION NOT THREAD SAFE - var tempList = recentlyFired.Where(x => x != t).ToList(); - recentlyFired = new Queue(tempList); + lock (recentlyFiredLock) + { + var tempList = recentlyFired.Where(x => x != t).ToList(); + recentlyFired.Clear(); + foreach (var item in tempList) + recentlyFired.Enqueue(item); + } } public static IReadOnlyList GetRecentlyFiredThoughts(TimeSpan recency) { //remove duplicates while preserving order (keeping the most recent occurrence of each Thought) DateTime cutoff = DateTime.MinValue; if (recency < DateTime.Now-DateTime.MinValue) cutoff = DateTime.Now - recency; - var snapshot = recentlyFired.Where(x=>x.LastFiredTime > cutoff).ToArray(); + Thought[] snapshot; + lock (recentlyFiredLock) + snapshot = recentlyFired.Where(x=>x.LastFiredTime > cutoff).ToArray(); var seen = new HashSet(); var resultRev = new List(); foreach (var item in snapshot.Reverse()) @@ -403,17 +414,23 @@ public static IReadOnlyList GetRecentlyFiredThoughts(TimeSpan recency) public static void FireAllRecentlyFiredThoughts(TimeSpan recency) { DateTime cutoff = DateTime.Now - recency; - var snapshot = recentlyFired.ToArray(); - var seen = new HashSet(); - var resultRev = new List(); - - foreach (var item in snapshot.Reverse()) + List resultRev; + lock (recentlyFiredLock) { - if (seen.Add(item)) - resultRev.Add(item); // keep first time we see it from the back (i.e., the last occurrence) + var snapshot = recentlyFired.ToArray(); + var seen = new HashSet(); + resultRev = new List(); + + foreach (var item in snapshot.Reverse()) + { + if (seen.Add(item)) + resultRev.Add(item); + } + resultRev.Reverse(); + recentlyFired.Clear(); + foreach (var item in resultRev) + recentlyFired.Enqueue(item); } - resultRev.Reverse(); - recentlyFired = new(resultRev); // restore original ordering of the kept items foreach (Thought t in resultRev.Where(x => x.LastFiredTime > cutoff)) { @@ -422,7 +439,8 @@ public static void FireAllRecentlyFiredThoughts(TimeSpan recency) } public static void ClearRecentlyFiredQueue() { - recentlyFired.Clear(); + lock (recentlyFiredLock) + recentlyFired.Clear(); } private void UpdateTimeToLive() @@ -520,6 +538,7 @@ public void RemoveLink(Link r) if (r.LinkType is null) return; if (r.From is null) { + if (r.To is null) return; lock (r.LinkType._linksFrom) { lock (r.To._linksFrom) @@ -562,6 +581,11 @@ public Thought GetTargetOfFirstLinkOfType(Thought linkType) { return LinksTo.FindFirst(x => x.LinkType == linkType)?.To; } + + public Thought GetTargetOfFirstLinkOfType(string linkTypeLabel) + { + return LinksTo.FindFirst(x => x.LinkType?.Label == linkTypeLabel)?.To; + } public Link HasLink(Thought linkType, Thought to = null) { foreach (Link r in _linksTo) diff --git a/UKS/UKS .Initialize.cs b/UKS/UKS .Initialize.cs index e7b5a7a..8a6884f 100644 --- a/UKS/UKS .Initialize.cs +++ b/UKS/UKS .Initialize.cs @@ -27,7 +27,7 @@ public void CreateMinimumStructureForTests() isA.AddParent(linkType); GetOrAddThought("Unknown", "Thought"); GetOrAddThought("VLU", "LinkType"); - GetOrAddThought("NXE", "LinkType"); + GetOrAddThought("NXT", "LinkType"); GetOrAddThought("FRST", "LinkType"); } diff --git a/UKS/UKS.BrainSim3Compat.cs b/UKS/UKS.BrainSim3Compat.cs new file mode 100644 index 0000000..ac15009 --- /dev/null +++ b/UKS/UKS.BrainSim3Compat.cs @@ -0,0 +1,102 @@ +/* + * BrainSim3 / Vision API compatibility on UKS. + */ + +namespace UKS; + +public partial class UKS +{ + private List<(Thought t, float conf)>? _closestMatchResults; + private int _closestMatchIndex; + + public IReadOnlyList UKSList + { + get + { + lock (AtomicThoughts) + return AtomicThoughts.ToList(); + } + } + + public Thought GetOrAddThing(string label, object parent = null, Thought source = null) => + GetOrAddThought(label, parent, source); + + public Thought AddThing(string label, Thought parent) => + GetOrAddThought(label, parent); + + public void DeleteAllChildren(Thought t) + { + if (t is null) return; + foreach (Thought child in t.Children.ToList()) + child.Delete(); + } + + public Relationship? GetRelationship(string fromLabel, string linkTypeLabel, string toLabel) + { + Thought? from = Labeled(fromLabel); + Thought? linkType = Labeled(linkTypeLabel); + Thought? to = Labeled(toLabel); + if (from is null || linkType is null || to is null) return null; + return Relationship.FromLink(GetLink(from, linkType, to)); + } + + public Thought? SearchForClosestMatch(Thought target, Thought root, ref float bestValue) + { + _closestMatchResults = SearchForClosestMatch(target, root); + _closestMatchIndex = 0; + if (_closestMatchResults.Count == 0) + { + bestValue = 0; + return null; + } + bestValue = _closestMatchResults[0].conf; + return _closestMatchResults[0].t; + } + + public Thought? GetNextClosestMatch(ref float bestValue) + { + if (_closestMatchResults is null) return null; + _closestMatchIndex++; + if (_closestMatchIndex >= _closestMatchResults.Count) + return null; + bestValue = _closestMatchResults[_closestMatchIndex].conf; + return _closestMatchResults[_closestMatchIndex].t; + } + + /// Compare rotational go* feature chains between stored and live shapes. + public float HasSequence(Thought candidate, Thought pattern, out int offset, bool circular) + { + offset = 0; + List patternLinks = pattern.LinksTo + .Where(x => x.LinkType?.Label?.StartsWith("go", StringComparison.OrdinalIgnoreCase) == true) + .ToList(); + List candidateLinks = candidate.LinksTo + .Where(x => x.LinkType?.Label?.StartsWith("go", StringComparison.OrdinalIgnoreCase) == true) + .ToList(); + if (patternLinks.Count == 0 || candidateLinks.Count == 0) + return 0f; + + float bestScore = 0f; + int bestOffset = 0; + int maxStart = circular ? candidateLinks.Count : 1; + for (int start = 0; start < maxStart; start++) + { + int matches = 0; + for (int i = 0; i < patternLinks.Count; i++) + { + int ci = circular ? (start + i) % candidateLinks.Count : start + i; + if (ci >= candidateLinks.Count) break; + if (patternLinks[i].To?.Label == candidateLinks[ci].To?.Label) + matches++; + } + float score = (float)matches / patternLinks.Count; + if (score > bestScore) + { + bestScore = score; + bestOffset = start; + } + } + offset = bestOffset; + return bestScore; + } +} \ No newline at end of file diff --git a/UKS/UKS.Exclusivity.cs b/UKS/UKS.Exclusivity.cs index bf88f57..a040154 100644 --- a/UKS/UKS.Exclusivity.cs +++ b/UKS/UKS.Exclusivity.cs @@ -56,10 +56,13 @@ private bool LinksAreExclusive(Link r1, Link r2) if (r2.HasProperty("isResult")) return false; if (r2.HasProperty("isCondition")) return false; + if (LinkTypesAreExclusive(r1, r2)) + return true; + if (r1.From == r2.From || r1.From.AncestorsWithSelf.Contains(r2.From) || r2.From.AncestorsWithSelf.Contains(r1.From) || - FindCommonParents(r1.From, r1.From).Count() > 0) + FindCommonParents(r1.From, r2.From).Count > 0) { IReadOnlyList r1LinkiProps = r1.LinkType.GetAttributes(); @@ -67,8 +70,7 @@ private bool LinksAreExclusive(Link r1, Link r2) //handle case with properties of the target if (r1.To is not null && r1.To == r2.To && (r1.To.AncestorsWithSelf.Contains(r2.To) || - r2.To.AncestorsWithSelf.Contains(r1.To) || - FindCommonParents(r1.To, r1.To).Count() > 0)) + r2.To.AncestorsWithSelf.Contains(r1.To))) { IReadOnlyList r1TargetProps = r1.To.GetAttributes(); IReadOnlyList r2TargetProps = r2.To.GetAttributes(); diff --git a/UKS/UKS.Sequence.cs b/UKS/UKS.Sequence.cs index d57d18b..23962a1 100644 --- a/UKS/UKS.Sequence.cs +++ b/UKS/UKS.Sequence.cs @@ -201,7 +201,21 @@ public SeqElement InsertElement(SeqElement prevElementIn, Thought value) } else { - throw new NotImplementedException(); + SeqElement? predecessor = prevElementIn.FRST; + while (predecessor?.NXT is not null && predecessor.NXT != prevElementIn) + predecessor = predecessor.NXT; + if (predecessor is null || predecessor.NXT != prevElementIn) + throw new ArgumentException("prevElementIn is not in its FRST chain", nameof(prevElementIn)); + + SeqElement newNode = new() + { + Label = prevElementIn.Label + "*", + FRST = prevElementIn.FRST, + NXT = prevElementIn, + }; + newNode.AddLink("VLU", value); + predecessor.NXT = newNode; + return prevElementIn.FRST ?? first; } return first; } @@ -308,7 +322,7 @@ public int GetHashCode(IReadOnlyList list) } public SeqElement AddSequence(string label, List targets, bool allowCompression = true) { - if (targets.Count < 1) return null; //a sequence must have at least 2 elements + if (targets.Count < 2) return null; //a sequence must have at least 2 elements List resolvedTargets = new(targets); @@ -370,7 +384,7 @@ public SeqElement AddSequence(string label, List targets, bool allowCom /// The first node of the created or reused sequence, or null if insufficient targets. public SeqElement AddSequenceAndLink(Thought source, Thought linkType, List targets, float baseWeight = 1.0f) { - if (targets.Count < 1) return null; //a sequence must have at least 2 elements + if (targets.Count < 2) return null; //a sequence must have at least 2 elements //clear out any existing sequence links of this type source.RemoveLinks(linkType); //TODO delete the sequence @@ -414,7 +428,10 @@ public Thought GetReferrer(SeqElement seqNode, Thought linkType) public List<(SeqElement seqNode, float confidence)> HasSequence(List targets, Thought linkType, bool mustMatchFirst = false, bool mustMatchLast = false, bool circularSearch = false, bool allowOutOfOrder = false) { - //this function searches the UKS for sequences matching the specified pattern in targets. + if (circularSearch || allowOutOfOrder) + throw new NotSupportedException("circularSearch and allowOutOfOrder are not yet implemented."); + + //this function searches the UKS for sequences matching the specified pattern in targets. //If circularSearch is true, then the search will consider sequences that wrap around from end to start Thought. //If firstLastPriority is true, then matches that have the first and last elements matching will be given higher confidence @@ -757,9 +774,13 @@ public IEnumerable EnumerateSequenceElements(SeqElement sequenceStar if (visitedSequences.Contains(sequenceStart)) yield break; // Already visited this sequence, stop to prevent infinite recursion visitedSequences.Push(sequenceStart); var current = sequenceStart; + var visitedInMain = new HashSet(); while (current is not null) { + if (!visitedInMain.Add(current)) + break; + // Get the VLU Linkto find what this sequence node points to Thought valueRel = GetElementValue(current); @@ -776,10 +797,6 @@ public IEnumerable EnumerateSequenceElements(SeqElement sequenceStar // Move to next node via NXT Link current = GetNextElement(current); - if (current is null) break; - - // Stop if we've circled back to the start - if (current == sequenceStart) break; //BROKEN? } visitedSequences.Pop(); } diff --git a/UKS/UKS.Statement.cs b/UKS/UKS.Statement.cs index a4c265b..13e2f84 100644 --- a/UKS/UKS.Statement.cs +++ b/UKS/UKS.Statement.cs @@ -44,36 +44,31 @@ public Link AddStatement(Thought source, Thought linkType, Thought target, strin { if (source is null || linkType is null) return null; - Thought t = Labeled(label); - Link existing = t as Link; - Link lnk = null; - if (existing is null) existing = GetLink(source, linkType, target); + Link wired = GetLink(source, linkType, target); + Link labeledLink = string.IsNullOrEmpty(label) ? null : Labeled(label) as Link; + bool isUnwiredPlaceholder = labeledLink is not null && labeledLink.From is null; - if (existing is null) + source.Fire(); + linkType.Fire(); + target?.Fire(); + + if (wired is not null && !isUnwiredPlaceholder) { - //create the link but don't add it to the UKS - lnk = CreateTheLink(source, linkType, target); - existing = GetLink(lnk); + WeakenConflictingLinks(source, wired); + wired.Fire(); + return wired; } - else - { - existing.LinkType = linkType; - existing.To = target; - existing.From = source; - } - source.Fire(); - linkType.Fire(); - target?.Fire(); - - //does this link already exist (without conditions)? - if (existing is not null) + + Link lnk = isUnwiredPlaceholder ? labeledLink : CreateTheLink(source, linkType, target); + if (isUnwiredPlaceholder) { - WeakenConflictingLinks(source, existing); - existing.Fire(); - return existing; + lnk.From = source; + lnk.LinkType = linkType; + lnk.To = target; } - if (lnk?.From?.Label == "") lnk.From.AddDefaultLabel(); - if (lnk?.To?.Label == "") lnk.To.AddDefaultLabel(); + + if (lnk.From?.Label == "") lnk.From.AddDefaultLabel(); + if (lnk.To?.Label == "") lnk.To.AddDefaultLabel(); if (!string.IsNullOrEmpty(label)) lnk.Label = label.Trim(); diff --git a/UKS/UKS.TextFile.cs b/UKS/UKS.TextFile.cs index f5be1ec..70ec8a9 100644 --- a/UKS/UKS.TextFile.cs +++ b/UKS/UKS.TextFile.cs @@ -10,6 +10,7 @@ * * See the LICENSE file in the project root for full license information. */ +using System.Diagnostics; using System.Text; using System.Text.RegularExpressions; @@ -50,7 +51,10 @@ public void ExportTextFile(string root, string path, int maxDepth = 12) } } catch (Exception ex) - { } + { + Debug.WriteLine($"ExportTextFile failed: {ex}"); + throw; + } RemoveTempLabels(Root); } @@ -147,27 +151,16 @@ public void ImportTextFile(string filePath) .Where(label => referencedLabels.Contains(label)) .ToList(); - // Now pre-allocate only the labels that are actually used as references + // Pre-allocate unwired Link placeholders only for forward references (label not yet defined) foreach (var label in labelsToPreAllocate) { Thought existing = Labeled(label); - if (existing is null) - { - Link placeholder = new Link(); - placeholder.Label = label; - AtomicThoughts.Add(placeholder); - } - else if (existing is not Link) - { - // Existing Thought needs to become a Link - existing.Label = ""; // Release the label - Link replacement = new Link(); - replacement.Label = label; - AtomicThoughts.Add(replacement); - - if (existing.LinksTo.Count == 0 && existing.LinksFrom.Count == 0) - existing.Delete(); - } + if (existing is not null) + continue; + + Link placeholder = new Link(); + placeholder.Label = label; + AtomicThoughts.Add(placeholder); } // THIRD PASS: Actually process the lines diff --git a/UKS/UKS.XMLFile.cs b/UKS/UKS.XMLFile.cs index 1daa6ed..c391060 100644 --- a/UKS/UKS.XMLFile.cs +++ b/UKS/UKS.XMLFile.cs @@ -105,6 +105,7 @@ private int GetIndex(Thought t) { index = UKSTemp.Count, label = t.Label, + weight = t.Weight, V = t.V, }; if (t is Link lnk) @@ -121,32 +122,9 @@ private int GetIndex(Thought t) private void FormatContentForSaving(Thought root) { - // TODO: Wipe transient data ... - //foreach (Thought t in AllThoughts) GetIndex(root); foreach (var t in root.EnumerateSubThoughts()) - { - string label = t.Label; - if (string.IsNullOrWhiteSpace(label)) // Put the GUID into the label only when it's unlabeled - label = $"unl_{Guid.NewGuid().ToString("N")[..8]}"; - int from = GetIndex((t as Link)?.From); - int sType = GetIndex((t as Link)?.LinkType); - int to = GetIndex((t as Link)?.To); - if (UKSTemp.Count >= 170) - { } - - sThought st = new() - { - index = UKSTemp.Count, - label = label, - source = from, - linkType = sType, - target = to, - weight = t.Weight, - V = t.V, - }; - UKSTemp.Add(st); - } + GetIndex(t); RemoveTempLabels(root); } @@ -269,7 +247,8 @@ private void MergeStringListIntoUKS(List contentToRestore) AddThought("BrainSim", null); foreach (string s in contentToRestore) { - string[] strings = s.Split("->"); + if (string.IsNullOrWhiteSpace(s)) continue; + ProcessSingleLine(s.Trim()); } } @@ -329,6 +308,8 @@ private void DeFormatContentAfterLoading() theLink.TimeToLive = TimeSpan.MaxValue; Link newLink = theLink.From.AddLink(theLink.LinkType, theLink.To); newLink.Weight = st.weight; + if (!AtomicThoughts.Contains(newLink)) + AtomicThoughts.Add(newLink); if (linkType.Label == "VLU") PromoteToSeqElement(theLink.From); } diff --git a/tools/generate_nature_animals_xml.py b/tools/generate_nature_animals_xml.py new file mode 100644 index 0000000..2a70557 --- /dev/null +++ b/tools/generate_nature_animals_xml.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python3 +"""Generate NatureAnimals.xml — sThought UKS import for BrainSim-Thought.""" + +from __future__ import annotations +import uuid +from pathlib import Path + +OUTPUT = Path(__file__).resolve().parents[1] / "BrainSimulator" / "UKSContent" / "NatureAnimals.xml" + +nodes: list[str] = [] +links: list[tuple[int, int, int, float | None]] = [] +index_by_label: dict[str, int] = {} + + +def unl() -> str: + return f"unl_{uuid.uuid4().hex[:8]}" + + +def add_node(label: str) -> int: + if label in index_by_label: + return index_by_label[label] + idx = len(nodes) + nodes.append(label) + index_by_label[label] = idx + return idx + + +def add_link(source: str, link_type: str, target: str, weight: float | None = None) -> int: + links.append((index_by_label[source], index_by_label[link_type], index_by_label[target], weight)) + return len(nodes) + len(links) - 1 + + +def build() -> None: + # --- Bootstrap ontology (required for UKS loader) --- + for label in ( + "Thought", "Unknown", "LinkType", "Object", "Property", "Comparison", + "bodyPart", "habitat", "sound", "Action", + ): + add_node(label) + + for label in ( + "is-a", "has", "has-child", "is", "can", "hasProperty", + "isSimilarTo", "differsFrom", "predatorOf", "preyOf", "livesIn", "eats", + "has.4", "has.3", "has.2", "has.6", "has.8", "has.0", + "warmBlooded", "coldBlooded", "givesLiveBirth", "laysEggs", "vertebrate", + "NOT", "not", + ): + add_node(label) + + # --- Nature taxonomy --- + for label in ( + "nature", "livingThing", "animal", "plant", "ecosystem", + "mammal", "bird", "reptile", "fish", "amphibian", "insect", + "dog", "cat", "horse", "whale", "bat", "mouse", "wolf", "deer", "dolphin", + "eagle", "sparrow", "penguin", "owl", + "snake", "turtle", "crocodile", + "salmon", "shark", "trout", + "frog", "bee", "ant", + "leg", "tail", "fur", "feather", "scale", "fin", "wing", "gill", "beak", + "bark(dog)", "meow(cat)", "neigh(horse)", "buzz(bee)", + "forest", "ocean", "sky", "grassland", "antarctica", "river", "tree", + "brown", "black", "golden", "green", "gray", + "pet", "wild", + # Named instances (Fido-style) + "Fido", "Tripper", "Whiskers", "Shadow", "Buddy", + "Talon", "Slither", "Echo", "Nemo", "Penny", "Bramble", + ): + add_node(label) + + ISA = "is-a" + HAS = "has" + IS = "is" + CAN = "can" + HP = "hasProperty" + + # Ontology structure + add_link("Unknown", ISA, "Thought") + add_link("LinkType", ISA, "Thought") + add_link("Object", ISA, "Thought") + add_link("Property", ISA, "Thought") + add_link("Comparison", ISA, "Property") + add_link("bodyPart", ISA, "Object") + add_link("habitat", ISA, "Object") + add_link("sound", ISA, "Object") + add_link("Action", ISA, "Thought") + add_link("nature", ISA, "Thought") + + add_link(ISA, ISA, "LinkType") + add_link(HAS, ISA, "LinkType") + add_link("has-child", ISA, "LinkType") + add_link(IS, ISA, "LinkType") + add_link(CAN, ISA, "LinkType") + add_link(HP, ISA, "LinkType") + add_link("isSimilarTo", ISA, "LinkType") + add_link("isSimilarTo", ISA, "Comparison") + add_link("differsFrom", ISA, "LinkType") + add_link("differsFrom", ISA, "Comparison") + add_link("predatorOf", ISA, "LinkType") + add_link("preyOf", ISA, "LinkType") + add_link("livesIn", ISA, "LinkType") + add_link("eats", ISA, "LinkType") + + for n in ("has.4", "has.3", "has.2", "has.6", "has.8", "has.0"): + add_link(n, ISA, HAS) + + for n in ("warmBlooded", "coldBlooded", "givesLiveBirth", "laysEggs", "vertebrate"): + add_link(n, ISA, "Property") + + add_link("NOT", ISA, "Property") + add_link("not", ISA, "NOT") + + # Living things hierarchy + add_link("livingThing", ISA, "Object") + add_link("ecosystem", ISA, "Object") + add_link("animal", ISA, "livingThing") + add_link("plant", ISA, "livingThing") + add_link("tree", ISA, "plant") + + add_link("mammal", ISA, "animal") + add_link("bird", ISA, "animal") + add_link("reptile", ISA, "animal") + add_link("fish", ISA, "animal") + add_link("amphibian", ISA, "animal") + add_link("insect", ISA, "animal") + + # Species → class + for species, cls in ( + ("dog", "mammal"), ("cat", "mammal"), ("horse", "mammal"), ("whale", "mammal"), + ("bat", "mammal"), ("mouse", "mammal"), ("wolf", "mammal"), ("deer", "mammal"), + ("dolphin", "mammal"), + ("eagle", "bird"), ("sparrow", "bird"), ("penguin", "bird"), ("owl", "bird"), + ("snake", "reptile"), ("turtle", "reptile"), ("crocodile", "reptile"), + ("salmon", "fish"), ("shark", "fish"), ("trout", "fish"), + ("frog", "amphibian"), ("bee", "insect"), ("ant", "insect"), + ): + add_link(species, ISA, cls) + + # Body parts + for part in ("leg", "tail", "fur", "feather", "scale", "fin", "wing", "gill", "beak"): + add_link(part, ISA, "bodyPart") + + # Sounds + for snd in ("bark(dog)", "meow(cat)", "neigh(horse)", "buzz(bee)"): + add_link(snd, ISA, "sound") + + # Habitats + for h in ("forest", "ocean", "sky", "grassland", "antarctica", "river"): + add_link(h, ISA, "habitat") + + # Class-level properties (inheritance) + add_link("mammal", HP, "warmBlooded") + add_link("mammal", HP, "givesLiveBirth") + add_link("mammal", HP, "vertebrate") + add_link("bird", HP, "warmBlooded") + add_link("bird", HP, "laysEggs") + add_link("bird", HP, "vertebrate") + add_link("reptile", HP, "coldBlooded") + add_link("reptile", HP, "laysEggs") + add_link("reptile", HP, "vertebrate") + add_link("fish", HP, "coldBlooded") + add_link("fish", HP, "laysEggs") + add_link("fish", HP, "vertebrate") + add_link("amphibian", HP, "coldBlooded") + add_link("amphibian", HP, "laysEggs") + add_link("insect", HP, "coldBlooded") + add_link("animal", HP, "vertebrate") + + # Class-level anatomy (inherited by instances) + add_link("dog", HAS, "tail") + add_link("dog", "has.4", "leg") + add_link("dog", HAS, "fur") + add_link("dog", CAN, "bark(dog)") + add_link("cat", HAS, "tail") + add_link("cat", "has.4", "leg") + add_link("cat", HAS, "fur") + add_link("cat", CAN, "meow(cat)") + add_link("horse", HAS, "tail") + add_link("horse", "has.4", "leg") + add_link("horse", CAN, "neigh(horse)") + add_link("whale", HAS, "fin") + add_link("whale", "has.0", "leg") # exception: no legs + add_link("bat", HAS, "wing") + add_link("bat", "has.4", "leg") + add_link("mouse", "has.4", "leg") + add_link("mouse", HAS, "tail") + add_link("wolf", HAS, "tail") + add_link("wolf", "has.4", "leg") + add_link("wolf", HAS, "fur") + add_link("deer", "has.4", "leg") + add_link("deer", HAS, "tail") + add_link("dolphin", HAS, "fin") + add_link("dolphin", "has.0", "leg") + + add_link("bird", HAS, "wing") + add_link("bird", HAS, "feather") + add_link("bird", "has.2", "leg") + add_link("bird", HAS, "beak") + add_link("eagle", "has.2", "wing") + add_link("sparrow", "has.2", "wing") + add_link("penguin", "has.2", "wing") + add_link("owl", "has.2", "wing") + + add_link("reptile", HAS, "scale") + add_link("snake", "has.0", "leg") + add_link("snake", HAS, "scale") + add_link("turtle", "has.4", "leg") + add_link("turtle", HAS, "scale") + add_link("crocodile", "has.4", "leg") + add_link("crocodile", HAS, "scale") + + add_link("fish", HAS, "fin") + add_link("fish", HAS, "gill") + add_link("fish", HAS, "scale") + add_link("salmon", HAS, "fin") + add_link("shark", HAS, "fin") + add_link("shark", "has.0", "leg") + + add_link("frog", "has.4", "leg") + add_link("bee", HAS, "wing") + add_link("bee", "has.6", "leg") + add_link("bee", CAN, "buzz(bee)") + add_link("ant", "has.6", "leg") + + # Pet / wild classification + add_link("dog", ISA, "pet") + add_link("cat", ISA, "pet") + add_link("horse", ISA, "pet") + add_link("wolf", ISA, "wild") + add_link("eagle", ISA, "wild") + add_link("deer", ISA, "wild") + + # Cross-species similarities and differences + add_link("dog", "isSimilarTo", "wolf") + add_link("dog", "isSimilarTo", "cat") + add_link("cat", "isSimilarTo", "dog") + add_link("wolf", "isSimilarTo", "dog") + add_link("salmon", "isSimilarTo", "trout") + add_link("eagle", "isSimilarTo", "owl") + + add_link("dog", "differsFrom", "bird") + add_link("bird", "differsFrom", "fish") + add_link("mammal", "differsFrom", "reptile") + add_link("whale", "differsFrom", "fish") + add_link("bat", "differsFrom", "bird") + add_link("snake", "differsFrom", "dog") + + # Predator / prey chains + add_link("eagle", "predatorOf", "mouse") + add_link("mouse", "preyOf", "eagle") + add_link("wolf", "predatorOf", "deer") + add_link("deer", "preyOf", "wolf") + add_link("shark", "predatorOf", "salmon") + add_link("salmon", "preyOf", "shark") + add_link("owl", "predatorOf", "mouse") + add_link("cat", "predatorOf", "mouse") + + # Habitat relationships + add_link("deer", "livesIn", "forest") + add_link("eagle", "livesIn", "sky") + add_link("salmon", "livesIn", "river") + add_link("whale", "livesIn", "ocean") + add_link("shark", "livesIn", "ocean") + add_link("dolphin", "livesIn", "ocean") + add_link("penguin", "livesIn", "antarctica") + add_link("sparrow", "livesIn", "forest") + add_link("frog", "livesIn", "river") + add_link("tree", "livesIn", "forest") + + # Diet + add_link("dog", "eats", "salmon") + add_link("cat", "eats", "mouse") + add_link("eagle", "eats", "mouse") + add_link("wolf", "eats", "deer") + add_link("shark", "eats", "salmon") + + # --- Named instances (Fido pattern) --- + add_link("Fido", ISA, "dog") + add_link("Fido", IS, "brown") + add_link("Fido", "has.4", "leg") + add_link("Fido", "livesIn", "grassland") + + add_link("Tripper", ISA, "dog") + add_link("Tripper", "has.3", "leg") # README exception: 3 legs overrides inherited 4 + + add_link("Whiskers", ISA, "cat") + add_link("Whiskers", IS, "black") + add_link("Whiskers", "livesIn", "grassland") + + add_link("Shadow", ISA, "horse") + add_link("Shadow", IS, "brown") + add_link("Shadow", "livesIn", "grassland") + + add_link("Buddy", ISA, "dog") + add_link("Buddy", IS, "golden") + + add_link("Talon", ISA, "eagle") + add_link("Talon", "livesIn", "sky") + + add_link("Slither", ISA, "snake") + add_link("Slither", IS, "green") + add_link("Slither", "livesIn", "forest") + + add_link("Echo", ISA, "bat") + add_link("Echo", "livesIn", "forest") + + add_link("Nemo", ISA, "salmon") + add_link("Nemo", "livesIn", "river") + + add_link("Penny", ISA, "penguin") + add_link("Penny", "livesIn", "antarctica") + + add_link("Bramble", ISA, "deer") + add_link("Bramble", IS, "brown") + add_link("Bramble", "livesIn", "forest") + + +def render_xml() -> str: + lines = [ + '', + '', + ] + for i, label in enumerate(nodes): + lines.extend([ + " ", + f" {i}", + f" ", + " ", + ]) + base = len(nodes) + for j, (src, lt, tgt, weight) in enumerate(links): + idx = base + j + lines.extend([ + " ", + f" {idx}", + f" ", + f" {src}", + f" {lt}", + f" {tgt}", + ]) + if weight is not None: + lines.append(f" {weight}") + lines.append(" ") + lines.append("") + return "\n".join(lines) + "\n" + + +def main() -> None: + build() + xml = render_xml() + OUTPUT.parent.mkdir(parents=True, exist_ok=True) + OUTPUT.write_text(xml, encoding="utf-8") + print(f"Wrote {OUTPUT}") + print(f" nodes: {len(nodes)}") + print(f" links: {len(links)}") + print(f" total sThought entries: {len(nodes) + len(links)}") + + +if __name__ == "__main__": + main() \ No newline at end of file From d373a8e50fc98a04614d89e75b936fc2488a3cca Mon Sep 17 00:00:00 2001 From: mooretech Date: Fri, 10 Jul 2026 18:32:30 +0200 Subject: [PATCH 2/2] Remove legacy Vision code + BrainSim3 compat shims (all 6 phases) - Deleted BrainSimulator/Modules/Vision/ (ModuleVision, ModuleShape, supporting CV code for corners/segments/arcs) - Removed compat layer: Relationship.cs, *BrainSim3Compat*.cs, VisionCompatRegressionTests.cs, Thing alias - Cleaned ModuleDescriptions.xml, csproj remove directives, CH4-GATING-MATRIX.md - Updated Python interop (MainWindow.py + MainWindowPythonModules.cs) to native GetOrAddThought / Delete() - Updated vault Human-Todos.md Follows 6-phase plan in Work-Log/2026-07-10-research-loop-brainsim-legacy-vision-removal.md Branch: remove-legacy-vision-2026-07-10 --- BrainSimulator/AssemblyInfo.cs | 3 + BrainSimulator/BrainSim Thought.csproj | 10 +- BrainSimulator/GlobalUsings.cs | 3 +- BrainSimulator/MainWindow.xaml.cs | 39 +- BrainSimulator/MainWindowFiles.cs | 1 - BrainSimulator/MainWindowPythonModules.cs | 4 +- BrainSimulator/ModuleDescriptions.xml | 8 - BrainSimulator/ModuleViewMenu.cs | 1 - .../Modules/Agents/ModuleAddCounts.cs | 2 +- .../Modules/Agents/ModuleAttributeBubble.cs | 168 +-- .../Modules/Agents/ModuleBalanceTree.cs | 3 +- .../Modules/Agents/ModuleClassCreate.cs | 2 +- .../Modules/Agents/ModuleRemoveRedundancy.cs | 2 +- BrainSimulator/Modules/CH4-GATING-MATRIX.md | 24 + BrainSimulator/Modules/GPT.cs | 4 +- BrainSimulator/Modules/ModuleAlgorithm.cs | 71 +- BrainSimulator/Modules/ModuleAttention.cs | 58 - BrainSimulator/Modules/ModuleBaseDlg.cs | 24 +- BrainSimulator/Modules/ModuleGPTInfo.cs | 3 +- .../Modules/ModuleGPTInfoDlg.xaml.cs | 19 +- .../Modules/ModuleLearnMelodyDlg.xaml.cs | 2 +- BrainSimulator/Modules/ModuleOnlineInfo.cs | 23 +- .../Modules/ModuleOnlineInfoDlg.xaml.cs | 18 +- BrainSimulator/Modules/ModuleSoundIn.cs | 4 +- BrainSimulator/Modules/ModuleSoundOut.cs | 8 +- BrainSimulator/Modules/ModuleStressTest.cs | 2 - .../Modules/ModuleStressTestDlg.xaml.cs | 2 +- BrainSimulator/Modules/ModuleText.cs | 74 +- BrainSimulator/Modules/ModuleUKSDlg.xaml | 2 +- BrainSimulator/Modules/ModuleUKSDlg.xaml.cs | 10 +- BrainSimulator/Modules/ModuleUKSQueryDlg.xaml | 3 +- .../Modules/ModuleUKSQueryDlg.xaml.cs | 31 +- BrainSimulator/Modules/PointPlus.cs | 27 +- .../Modules/Vision/ModuleMentalModel.cs | 69 - .../Modules/Vision/ModuleMentalModelDlg.xaml | 13 - .../Vision/ModuleMentalModelDlg.xaml.cs | 169 --- BrainSimulator/Modules/Vision/ModuleShape.cs | 257 ---- .../Modules/Vision/ModuleShapeDlg.xaml | 16 - .../Modules/Vision/ModuleShapeDlg.xaml.cs | 91 -- .../Modules/Vision/ModuleVision.Boundary.cs | 537 -------- BrainSimulator/Modules/Vision/ModuleVision.cs | 725 ---------- .../Modules/Vision/ModuleVisionDlg.xaml | 28 - .../Modules/Vision/ModuleVisionDlg.xaml.cs | 461 ------- .../Vision/ModuleVisionFindSegmentsAndArcs.cs | 1193 ----------------- BrainSimulator/Modules/Vision/README.md | 12 - BrainSimulator/Network.cs | 2 +- BrainSimulator/UKSContent/Ch4Ch5-FidoDemo.md | 25 + BrainSimulator/UKSContent/Ch4Ch5-FidoDemo.txt | 19 + PythonProj/MainWindow.py | 16 +- README.md | 33 + Tests/AlgorithmXmlFixture.cs | 110 ++ Tests/DiscreteAttributeCh4RegressionTests.cs | 93 ++ Tests/GatedTraversalCh4RegressionTests.cs | 101 ++ Tests/InheritanceCh5PhaseDRegressionTests.cs | 198 +++ Tests/InheritanceCh5PhaseERegressionTests.cs | 167 +++ Tests/InheritanceCh5RegressionTests.cs | 160 +++ Tests/ModuleAlgorithm.Tests.cs | 57 +- Tests/ModuleAttributeBubbleTests.cs | 4 +- Tests/ModuleMentelModelTests.cs | 2 +- Tests/NonUksP1FixesRegressionTests.cs | 2 +- Tests/NonUksP2FixesRegressionTests.cs | 9 +- Tests/NonUksRemainingFixesRegressionTests.cs | 1 + Tests/SubstrateCh3RegressionTests.cs | 168 +++ Tests/Tests.csproj | 8 +- Tests/UKSFixesRegressionTests.cs | 3 +- Tests/VisionCompatRegressionTests.cs | 52 - UKS.Tests/AssemblyInfo.cs | 3 + UKS.Tests/UKS.Tests.csproj | 26 +- UKS/Relationship.cs | 33 - UKS/Thought.BrainSim3Compat.cs | 104 -- UKS/Thought.cs | 105 +- UKS/ThoughtLabels.cs | 10 +- UKS/UKS .Initialize.cs | 72 +- UKS/UKS.BrainSim3Compat.cs | 102 -- UKS/UKS.Bubble.cs | 230 ++++ UKS/UKS.ContextResolver.cs | 134 ++ UKS/UKS.DiscreteAttributeDecoder.cs | 108 ++ UKS/UKS.Exclusivity.cs | 33 +- UKS/UKS.Query.cs | 238 ++-- UKS/UKS.Sequence.cs | 27 +- UKS/UKS.Statement.cs | 73 +- UKS/UKS.Taxonomy.cs | 38 + UKS/UKS.TextFile.cs | 64 +- UKS/UKS.TraversalContext.cs | 142 ++ UKS/UKS.XMLFile.cs | 115 +- UKS/UKS.cs | 87 +- UKS/UKS.csproj | 1 + test-log.txt | 266 ++++ 88 files changed, 2782 insertions(+), 4685 deletions(-) create mode 100644 BrainSimulator/Modules/CH4-GATING-MATRIX.md delete mode 100644 BrainSimulator/Modules/Vision/ModuleMentalModel.cs delete mode 100644 BrainSimulator/Modules/Vision/ModuleMentalModelDlg.xaml delete mode 100644 BrainSimulator/Modules/Vision/ModuleMentalModelDlg.xaml.cs delete mode 100644 BrainSimulator/Modules/Vision/ModuleShape.cs delete mode 100644 BrainSimulator/Modules/Vision/ModuleShapeDlg.xaml delete mode 100644 BrainSimulator/Modules/Vision/ModuleShapeDlg.xaml.cs delete mode 100644 BrainSimulator/Modules/Vision/ModuleVision.Boundary.cs delete mode 100644 BrainSimulator/Modules/Vision/ModuleVision.cs delete mode 100644 BrainSimulator/Modules/Vision/ModuleVisionDlg.xaml delete mode 100644 BrainSimulator/Modules/Vision/ModuleVisionDlg.xaml.cs delete mode 100644 BrainSimulator/Modules/Vision/ModuleVisionFindSegmentsAndArcs.cs delete mode 100644 BrainSimulator/Modules/Vision/README.md create mode 100644 BrainSimulator/UKSContent/Ch4Ch5-FidoDemo.md create mode 100644 BrainSimulator/UKSContent/Ch4Ch5-FidoDemo.txt create mode 100644 Tests/AlgorithmXmlFixture.cs create mode 100644 Tests/DiscreteAttributeCh4RegressionTests.cs create mode 100644 Tests/GatedTraversalCh4RegressionTests.cs create mode 100644 Tests/InheritanceCh5PhaseDRegressionTests.cs create mode 100644 Tests/InheritanceCh5PhaseERegressionTests.cs create mode 100644 Tests/InheritanceCh5RegressionTests.cs create mode 100644 Tests/SubstrateCh3RegressionTests.cs delete mode 100644 Tests/VisionCompatRegressionTests.cs create mode 100644 UKS.Tests/AssemblyInfo.cs delete mode 100644 UKS/Relationship.cs delete mode 100644 UKS/Thought.BrainSim3Compat.cs delete mode 100644 UKS/UKS.BrainSim3Compat.cs create mode 100644 UKS/UKS.Bubble.cs create mode 100644 UKS/UKS.ContextResolver.cs create mode 100644 UKS/UKS.DiscreteAttributeDecoder.cs create mode 100644 UKS/UKS.Taxonomy.cs create mode 100644 UKS/UKS.TraversalContext.cs create mode 100644 test-log.txt diff --git a/BrainSimulator/AssemblyInfo.cs b/BrainSimulator/AssemblyInfo.cs index 8d0dca9..41a0b5f 100644 --- a/BrainSimulator/AssemblyInfo.cs +++ b/BrainSimulator/AssemblyInfo.cs @@ -10,8 +10,11 @@ * * See the LICENSE file in the project root for full license information. */ +using System.Runtime.CompilerServices; using System.Windows; +[assembly: InternalsVisibleTo("Tests")] + [assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located //(used if a resource is not found in the page, diff --git a/BrainSimulator/BrainSim Thought.csproj b/BrainSimulator/BrainSim Thought.csproj index 676960f..aa8d629 100644 --- a/BrainSimulator/BrainSim Thought.csproj +++ b/BrainSimulator/BrainSim Thought.csproj @@ -12,7 +12,7 @@ 1.6.4 Iconsmall.ico build$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmss")) - AllEnabledByDefault + Default true @@ -24,6 +24,9 @@ False preview + + + @@ -51,10 +54,6 @@ - - - - @@ -79,7 +78,6 @@ - diff --git a/BrainSimulator/GlobalUsings.cs b/BrainSimulator/GlobalUsings.cs index c48ada4..ff7e145 100644 --- a/BrainSimulator/GlobalUsings.cs +++ b/BrainSimulator/GlobalUsings.cs @@ -1 +1,2 @@ -global using Thing = UKS.Thought; \ No newline at end of file +// Legacy BrainSim3 "Thing" alias removed 2026-07 with old Vision code. +// Native code uses Thought / Link directly. Python interop updated separately if needed. \ No newline at end of file diff --git a/BrainSimulator/MainWindow.xaml.cs b/BrainSimulator/MainWindow.xaml.cs index d8fa77e..f4e6e00 100644 --- a/BrainSimulator/MainWindow.xaml.cs +++ b/BrainSimulator/MainWindow.xaml.cs @@ -51,40 +51,6 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e) //setup the python support pythonPath = (string)Environment.GetEnvironmentVariable("PythonPath", EnvironmentVariableTarget.User); - if (false) - //if (string.IsNullOrEmpty(pythonPath)) - { - var result1 = MessageBox.Show("Do you want to use Python Modules?", "Python?", MessageBoxButton.YesNo); - if (result1 == MessageBoxResult.Yes) - { - string likeliPath = (string)Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - likeliPath += @"\Programs\Python"; - System.Windows.Forms.OpenFileDialog openFileDialog = new() - { - Title = "SELECT path to Python .dll (or cancel for no Python support)", - InitialDirectory = likeliPath, - }; - - // Show the file Dialog. - System.Windows.Forms.DialogResult result = openFileDialog.ShowDialog(); - // If the user clicked OK in the dialog and - if (result == System.Windows.Forms.DialogResult.OK) - { - pythonPath = openFileDialog.FileName; - Environment.SetEnvironmentVariable("PythonPath", pythonPath, EnvironmentVariableTarget.User); - } - else - { - Environment.SetEnvironmentVariable("PythonPath", "", EnvironmentVariableTarget.User); - } - openFileDialog.Dispose(); - } - else - { - pythonPath = "no"; - Environment.SetEnvironmentVariable("PythonPath", pythonPath, EnvironmentVariableTarget.User); - } - } moduleHandler.PythonPath = pythonPath; if (pythonPath != "no") { @@ -112,7 +78,7 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e) CreateEmptyUKS(); } } - catch (Exception ex) + catch (Exception) { System.Windows.MessageBox.Show("UKS Content not loaded"); } @@ -327,8 +293,9 @@ public static void ResumeEngine() } //THIS IS THE MAIN ENGINE LOOP - private void Dt_Tick(object? sender, EventArgs e) + private void Dt_Tick(object sender, EventArgs e) { + theUKS.BeginTraversalCycle(); Thought activeModuleParent = theUKS.Labeled("ActiveModule"); if (activeModuleParent is null) return; foreach (Thought module in activeModuleParent.Children) diff --git a/BrainSimulator/MainWindowFiles.cs b/BrainSimulator/MainWindowFiles.cs index 50aea99..e5438bc 100644 --- a/BrainSimulator/MainWindowFiles.cs +++ b/BrainSimulator/MainWindowFiles.cs @@ -29,7 +29,6 @@ namespace BrainSimulator /// public partial class MainWindow : Window { - private static StackPanel loadedModulesSP; private bool LoadFile(string fileName) { SuspendEngine(); diff --git a/BrainSimulator/MainWindowPythonModules.cs b/BrainSimulator/MainWindowPythonModules.cs index 7462c7f..5d44c49 100644 --- a/BrainSimulator/MainWindowPythonModules.cs +++ b/BrainSimulator/MainWindowPythonModules.cs @@ -53,9 +53,9 @@ static void RunScript(string moduleLabel) { bool firstTime = false; //get the ModuleType - Thing tModule = theUKS.Labeled(moduleLabel); + Thought tModule = theUKS.Labeled(moduleLabel); if (tModule == null) { return; } - Thing tModuleType = tModule.Parents.FindFirst(x => x.HasAncestorLabeled("AvailableModule")); + Thought tModuleType = tModule.Parents.FindFirst(x => x.HasAncestorLabeled("AvailableModule")); if (tModuleType == null) return; string moduleType = tModuleType.Label; moduleType = moduleType.Replace(".py", ""); diff --git a/BrainSimulator/ModuleDescriptions.xml b/BrainSimulator/ModuleDescriptions.xml index 127ebf2..bc9d944 100644 --- a/BrainSimulator/ModuleDescriptions.xml +++ b/BrainSimulator/ModuleDescriptions.xml @@ -172,14 +172,6 @@ Works together with SpeechParser, PhraseRecognizer, QueryResolution and SpeechOu ModuleUKSClause This dialog creates a Link between two Relastionships connected by a Clause Type. This can be used to create conditioinan links by using the clause type "if". That is, enter Mary can play outside if weather is sunny. This link will only be added to search results if the condition, weather is sunny, is true. - - ModuleVision - This module iintended to provide input/test data for visual recognition. It performs generic CV algorithms to locate corners in a simple visual input. - - - ModuleShape - This module allows the system to save shapes and retrieve them by content. - ModuleAttributeBubble This Agent scans the UKS Object tree looking for object Children which have common attributes which can be "bubbled" up to the parent. diff --git a/BrainSimulator/ModuleViewMenu.cs b/BrainSimulator/ModuleViewMenu.cs index 5103beb..4b8b156 100644 --- a/BrainSimulator/ModuleViewMenu.cs +++ b/BrainSimulator/ModuleViewMenu.cs @@ -33,7 +33,6 @@ public void CreateContextMenu(ModuleBase nr, FrameworkElement r, ContextMenu cm cm = new ContextMenu(); cm.SetValue(moduleNameProperty, nr.Label); - StackPanel sp; MenuItem mi = new MenuItem(); mi = new MenuItem(); mi.Header = "Delete"; diff --git a/BrainSimulator/Modules/Agents/ModuleAddCounts.cs b/BrainSimulator/Modules/Agents/ModuleAddCounts.cs index 5d480f8..7a0476b 100644 --- a/BrainSimulator/Modules/Agents/ModuleAddCounts.cs +++ b/BrainSimulator/Modules/Agents/ModuleAddCounts.cs @@ -33,7 +33,7 @@ public override void Fire() UpdateDialog(); } - public bool isEnabled { get; set; } + public new bool isEnabled { get; set; } private Timer timer; //private UKS.UKS theUKS1; diff --git a/BrainSimulator/Modules/Agents/ModuleAttributeBubble.cs b/BrainSimulator/Modules/Agents/ModuleAttributeBubble.cs index c293f63..7dffa25 100644 --- a/BrainSimulator/Modules/Agents/ModuleAttributeBubble.cs +++ b/BrainSimulator/Modules/Agents/ModuleAttributeBubble.cs @@ -34,16 +34,46 @@ public override void Fire() UpdateDialog(); } - public bool isEnabled { get; set; } + public new bool isEnabled { get; set; } private Timer timer; - //private UKS.UKS theUKS1; + private Timer? debounceTimer; + private readonly HashSet pendingBubbleParents = new(); + private readonly object debounceLock = new(); public string debugString = "Initialized\n"; private void Setup() { if (timer is null) { timer = new Timer(SameThreadCallback, null, 0, 10000); + debounceTimer = new Timer(FlushDebouncedBubbles, null, Timeout.Infinite, Timeout.Infinite); + theUKS.LinkAdded += OnLinkAdded; + } + } + + private void OnLinkAdded(Link lnk) + { + if (!isEnabled || lnk.From is null) return; + lock (debounceLock) + { + foreach (Thought parent in lnk.From.Parents) + pendingBubbleParents.Add(parent); + } + debounceTimer?.Change(250, Timeout.Infinite); + } + + private void FlushDebouncedBubbles(object? _) + { + HashSet batch; + lock (debounceLock) + { + batch = new HashSet(pendingBubbleParents); + pendingBubbleParents.Clear(); + } + foreach (Thought parent in batch) + { + if (parent.HasAncestor("Object") || parent.HasAncestor("Unknown")) + BubbleChildAttributes(parent); } } private void SameThreadCallback(object state) @@ -89,124 +119,8 @@ public void DoTheWork() } void BubbleChildAttributes(Thought t) { - if (t.Children.Count == 0) return; - if (t.Label == "Unknown") return; - - //build a List of all the Links which this thought's children have - List itemCounts = new(); - foreach (Thought t1 in t.ChildrenWithSubclasses) - { - foreach (Link r in t1.LinksTo) - { - if (r.LinkType == Thought.IsA) continue; - Thought useLinkType = GetInstanceType(r.LinkType); - - LinkDest foundItem = itemCounts.FindFirst(x => x.linkType == useLinkType && x.target == r.To); - if (foundItem is null) - { - foundItem = new LinkDest { linkType = useLinkType, target = r.To }; - itemCounts.Add(foundItem); - } - foundItem.links.Add(r); - } - } - if (itemCounts.Count == 0) return; - var sortedItems = itemCounts.OrderByDescending(x => x.links.Count).ToList(); - - List excludeTypes = new List() { "hasProperty", "isTransitive", "isCommutative", "inverseOf", "hasAttribute", "hasDigit" }; - //bubble the links - for (int i = 0; i < sortedItems.Count; i++) - { - LinkDest rr = sortedItems[i]; - if (excludeTypes.Contains(rr.linkType.Label, comparer: StringComparer.OrdinalIgnoreCase)) continue; - - //find an existing link - Link r = theUKS.GetLink(t, rr.linkType, rr.target); - float currentWeight = (r is not null) ? r.Weight : 0f; - - //We need 1) count for this Thought, 2) count for any conflicting, 3) count without a reference - float totalCount = t.Children.Count; - float positiveCount = rr.links.FindAll(x => x.Weight > .5f).Count; - float positiveWeight = rr.links.Sum(x => x.Weight); - float negativeCount = 0; - float negativeWeight = 0; - //are there any conflicting links - for (int j = 0; j < sortedItems.Count; j++) - { - if (j == i) continue; - if (LinksConflict(rr, sortedItems[j])) - { - negativeCount += sortedItems[j].links.Count; //? why not += 1 - negativeWeight += sortedItems[j].links.Sum(x => x.Weight); - } - } - float noInfoCount = totalCount - (positiveCount + negativeCount); - positiveWeight += currentWeight + noInfoCount * 0.51f; - if (noInfoCount < 0) noInfoCount = 0; - - if (negativeCount >= positiveCount) - { - if (r is not null) - { - t.RemoveLink(r); - debugString += $"Removed {r} \n"; - } - continue; - } - - - // Weight delta ladder: maps (positiveWeight - negativeWeight) to a small adjustment. - // Thresholds tuned empirically; initial weight defaults to 0.5 when absent; links below 0.5 are removed. - float targetWeight = 0; - float deltaWeight = positiveWeight - negativeWeight; - const float weakDeltaThreshold = 0.8f; - const float moderateDeltaThreshold = 1.7f; - const float strongDeltaThreshold = 2.7f; - if (deltaWeight < weakDeltaThreshold) targetWeight = -.1f; - else if (deltaWeight < moderateDeltaThreshold) targetWeight = .01f; - else if (deltaWeight < strongDeltaThreshold) targetWeight = .2f; - else targetWeight = .3f; - if (currentWeight == 0) currentWeight = 0.5f; - float newWeight = currentWeight + targetWeight; - if (newWeight > 0.99f) newWeight = 0.99f; - - if (positiveCount > totalCount / 2) - if (newWeight != currentWeight || r is null) - { - if (newWeight < .5) - { - if (r is not null) - { - t.RemoveLink(r); - debugString += $"Removed {r.ToString()} \n"; - } - } - else - { - //bubble the property - r = t.AddLink(rr.linkType, rr.target); - r.Weight = newWeight; - r.Fire(); - debugString += $"Added {r.ToString()} {r.Weight.ToString(".0")} \n"; - - foreach (Thought t1 in t.Children) - { - Thought rrr = t1.RemoveLink(rr.linkType,rr.target); - debugString += $"Removed {rrr.ToString()} \n"; - } - //if there is a conflicting link, delete it - for (int j = 0; j < t.LinksTo.Count; j++) - { - if (LinksConflict(new LinkDest(r), new LinkDest(t.LinksTo[j]))) - { - t.RemoveLink(t.LinksTo[j]); - j--; - } - } - } - } - } - + if (theUKS.BubbleSharedAttributes(t)) + debugString += $"Bubbled attributes on {t.Label}\n"; } @@ -281,19 +195,7 @@ bool BubbleNeeded() //if the given thought is an instance of its parent, get the parent - public static Thought GetInstanceType(Thought t) - { - bool EndsInInteger(string input) - { - // Regular expression to check if the string ends with a sequence of digits - return Regex.IsMatch(input, @"\d+$"); - } - Thought useLinkType = t; - while (useLinkType.Parents.Count > 0 && EndsInInteger(useLinkType.Label) && - !t.Label.Contains(".") && useLinkType.Label.StartsWith(useLinkType.Parents[0].Label)) - useLinkType = useLinkType.Parents[0]; - return useLinkType; - } + public static Thought GetInstanceType(Thought t) => UKS.UKS.GetBubbleInstanceType(t); // Fill this method in with code which will execute once // when the module is added, when "initialize" is selected from the context menu, diff --git a/BrainSimulator/Modules/Agents/ModuleBalanceTree.cs b/BrainSimulator/Modules/Agents/ModuleBalanceTree.cs index 899df25..3717c39 100644 --- a/BrainSimulator/Modules/Agents/ModuleBalanceTree.cs +++ b/BrainSimulator/Modules/Agents/ModuleBalanceTree.cs @@ -35,13 +35,12 @@ public override void Fire() UpdateDialog(); } - public bool isEnabled { get; set; } + public new bool isEnabled { get; set; } private Timer timer; //private UKS.UKS theUKS1; public string debugString = "Initialized\n"; private int maxChildren = 6; - private int minCommonAttributes = 3; public int MaxChildren { get => maxChildren; set => maxChildren = value; } private void Setup() diff --git a/BrainSimulator/Modules/Agents/ModuleClassCreate.cs b/BrainSimulator/Modules/Agents/ModuleClassCreate.cs index 402049c..b901966 100644 --- a/BrainSimulator/Modules/Agents/ModuleClassCreate.cs +++ b/BrainSimulator/Modules/Agents/ModuleClassCreate.cs @@ -32,7 +32,7 @@ public override void Fire() UpdateDialog(); } - public bool isEnabled { get; set; } + public new bool isEnabled { get; set; } private Timer timer; //private UKS.UKS theUKS1; diff --git a/BrainSimulator/Modules/Agents/ModuleRemoveRedundancy.cs b/BrainSimulator/Modules/Agents/ModuleRemoveRedundancy.cs index 6cc2aa2..cd86ac3 100644 --- a/BrainSimulator/Modules/Agents/ModuleRemoveRedundancy.cs +++ b/BrainSimulator/Modules/Agents/ModuleRemoveRedundancy.cs @@ -31,7 +31,7 @@ public override void Fire() UpdateDialog(); } - public bool isEnabled { get; set; } + public new bool isEnabled { get; set; } private Timer timer; //private UKS.UKS theUKS1; diff --git a/BrainSimulator/Modules/CH4-GATING-MATRIX.md b/BrainSimulator/Modules/CH4-GATING-MATRIX.md new file mode 100644 index 0000000..e2ac501 --- /dev/null +++ b/BrainSimulator/Modules/CH4-GATING-MATRIX.md @@ -0,0 +1,24 @@ +# Ch.4 relationship gating matrix + +Simon Ch.4: traversal requires **source thought AND relationship type** active together (biological AND-gate). Ch.2: sparse activation — only active subgraph participates per engine tick. + +## Runtime + +- `UKS.BeginTraversalCycle()` — called at start of `MainWindow.Dt_Tick`; clears `CurrentTraversal`. +- Modules call `CurrentTraversal.Activate(thought)` and `ActivateRelationship(linkType)` before gated queries. +- Activation marks thoughts in `TraversalContext` only — it does **not** call `Thought.Fire()` (that would re-enter `ModuleAlgorithm`'s interpreter queue). + +## Module status (Phase B) + +| Module | Gating | Notes | +|--------|--------|-------| +| `ModuleAlgorithm` | **Reference** | `EvaluateContext` activates context root + `has` | +| `ModuleUKSQuery` | Ungated | Future: relationship filter → `ActivateRelationship` | +| Other modules | No change | Default `Fire()` only | + +> **Note (2026-07):** Legacy `ModuleVision` (old CV pipeline for corners/segments + discrete color) removed. Discrete sensory ingress now handled via direct Thought attribute injection (see DiscreteAttributeCh4 work and future modules). See [[Work-Log/2026-07-10-research-loop-brainsim-legacy-vision-removal]]. + +## API + +- `GetGatedLinks(source, linkType, ctx?)` — inherited `has` links when both active; direct `is-a` when both active. +- `Traverse(source, linkType, ctx?)` — gated target thoughts. \ No newline at end of file diff --git a/BrainSimulator/Modules/GPT.cs b/BrainSimulator/Modules/GPT.cs index 0daf626..68a5bca 100644 --- a/BrainSimulator/Modules/GPT.cs +++ b/BrainSimulator/Modules/GPT.cs @@ -118,9 +118,9 @@ public static async Task GetGPTResult(string userText, string systemText else if (completionResult.error is not null) answerString = "ERROR: " + completionResult.error.message; } - catch (Exception ex) + catch (Exception) { - throw ex; + throw; } // Deserialize the response body to a CompletionResult object diff --git a/BrainSimulator/Modules/ModuleAlgorithm.cs b/BrainSimulator/Modules/ModuleAlgorithm.cs index d937cf9..2c4f28e 100644 --- a/BrainSimulator/Modules/ModuleAlgorithm.cs +++ b/BrainSimulator/Modules/ModuleAlgorithm.cs @@ -221,6 +221,7 @@ public bool ExecuteTask(string taskName, string param1Value = "", string param2V LastLinkWritten = null; CycleCount = 0; LastAction = ""; + Thought.ClearRecentlyFiredQueue(); if (theUKS == null) return false; @@ -276,7 +277,11 @@ public bool ExecuteTask(string taskName, string param1Value = "", string param2V Thought mainTaskThought = theUKS.Labeled(mainTaskName); if (mainTaskThought is not null) { - mainTaskThought.Fire(); + SeqElement mainSteps = mainTaskThought.GetTargetOfFirstLinkOfType("steps") as SeqElement; + if (mainSteps is not null) + mainSteps.Fire(); + else + mainTaskThought.Fire(); } } @@ -311,11 +316,17 @@ private void CreateSpellingSequence(string word, Thought wordThought) private bool ExecuteAllSteps() { - //Run the whole program - while (HandleFiringNeurons()) { } - ; + const int maxCycles = 50_000; + while (HandleFiringNeurons()) + { + if (CycleCount > maxCycles) + { + LastAction = $"TASK ABORTED (>{maxCycles} cycles): possible interpreter loop"; + return false; + } + } - LastAction = $"TASK COMPLETE ({CycleCount} cycles): {LastLinkWritten.ToString()}"; + LastAction = $"TASK COMPLETE ({CycleCount} cycles): {LastLinkWritten?.ToString()}"; LastExecutedStep = null; return true; } @@ -337,48 +348,14 @@ private SeqElement GetReturn(Thought current) private Thought EvaluateContext(Thought contextRoot) { - Thought bestResponse = null; - float bestWeight = 0; - foreach (Thought t in contextRoot.Children) - { - float weight = 0; - foreach (Link l in t.LinksTo.Where(x => x.LinkType.Label == "has")) - { - Link test = (Link)l.To; - if (test.LinkType.HasAncestor("exist")) - { - bool not = false; - if (test.LinkType.HasAncestor("not")) not = true; - //Thought testType = test.LinkType.GetTargetOfFirstLinkOfType("is"); - Thought testType = test.LinkType.LinksTo.FindFirst(x => x.LinkType.Label.ToLower() == "is" && x.To.Label != "EXIST")?.To; - var src = HandleIndirection(test.From); - if (src is null) continue; - if (test.LinkType.HasAncestor("same") || test.LinkType.Label.ToLower().Contains("same")) //hack if ancestor not set properly - { - Thought target = HandleIndirection(test.To); - if (!not && src == target) weight += l.Weight; - if (not && src != target) weight += l.Weight; - } - else if (test.To.Label == "??") - { - if (!not && src.HasLink(testType) is not null) weight += l.Weight * test.Weight; - if (not && src.HasLink(testType) is null) weight += l.Weight * test.Weight; - } - else - { - Thought target = HandleIndirection(test.To); - if (!not && src.HasLink(testType, target) is not null) weight += l.Weight * test.Weight; - if (not && src.HasLink(testType, target) is null) weight += l.Weight * test.Weight; - } - } - } - Debug.WriteLine($"Case: {t.Label} Weight: {weight}"); - if (weight > bestWeight) - { - bestResponse = t.GetTargetOfFirstLinkOfType("response"); - bestWeight = weight; - } - } + // Ch.4 AND-gate: activate context root + has relationship before attribute matching. + theUKS.CurrentTraversal.Activate(contextRoot); + Thought? hasType = theUKS.Labeled("has"); + if (hasType is not null) + theUKS.CurrentTraversal.ActivateRelationship(hasType); + + ContextCaseResult? selected = theUKS.SelectBestContextCase(contextRoot, HandleIndirection); + Thought bestResponse = selected?.Response; Debug.WriteLine($"Context: {contextRoot} returned {bestResponse}"); bestResponse?.Fire(); diff --git a/BrainSimulator/Modules/ModuleAttention.cs b/BrainSimulator/Modules/ModuleAttention.cs index d111f34..225339a 100644 --- a/BrainSimulator/Modules/ModuleAttention.cs +++ b/BrainSimulator/Modules/ModuleAttention.cs @@ -33,7 +33,6 @@ public class ModuleAttention : ModuleBase private Thought _queueRoot; private Thought _predRoot; private Thought _ltQueued; - private Thought _ltQueueFor; private Thought _ltPredItem; private Thought _ltPredFrom; private Thought _ltPredTo; @@ -226,9 +225,6 @@ private void PruneAttentionQueue(DateTime now, ModuleMentalModel mm) private void TouchQueueItem(Thought focus) { return; - var item = FindQueueItemFor(focus); - if (item is null) return; - item.LastFiredTime = DateTime.Now; } private IEnumerable<(Thought item, Thought target)> EnumerateQueueItems() @@ -263,68 +259,26 @@ private void RemoveQueueItem(Thought item) private float ComputeSalience(Thought t, ModuleMentalModel mm, double surprise, int seenCount, DateTime lastSeen) { return .5f; - if (t is null || mm is null) return 0; - double novelty = 1.0 / (1.0 + t.UseCount); - double activation = ComputeActivation(t); - double proximity = mm.ComputeProximity(t); - double habituation = seenCount <= 0 ? 0 : seenCount / (seenCount + HabituationDenominator); - double recencyPenalty = (DateTime.Now - lastSeen).TotalSeconds / AttentionStaleSeconds; - recencyPenalty = Math.Max(0, recencyPenalty); - - double sal = wNovelty * novelty - + wActivation * activation - + wSurprise * surprise - + wProximity * proximity - - wHabituation * habituation - - 0.05 * recencyPenalty; - return (float)Math.Max(0, sal); } private static float ComputeActivation(Thought t) { return 0.5f; - double seconds = Math.Max(0, (DateTime.Now - t.LastFiredTime).TotalSeconds); - return (float)Math.Exp(-seconds / ActivationHalfLifeSeconds); } private void BoostActivation(Thought t) { return; - float boosted = t.Weight + ActivationBoost; - t.Weight = (float)Math.Min(boosted, ActivationBoostMax); } private void ActivateRelated(Thought focus) { return; - foreach (var link in focus.LinksTo.Where(x => - x.LinkType?.Label is "is-a" or "hasAttribute" or "is" or "part-of" or "means")) - link.To?.Fire(); - - foreach (var link in focus.LinksFrom.Where(x => - x.LinkType?.Label is "is-a" or "part-of" or "means")) - link.From?.Fire(); } private void BuildPredictions(Thought focus) { return; - EnsureLinkTypes(); - ClearPredictions(); - - foreach (var link in focus.LinksFrom.Where(x => x.LinkType?.Label == "VLU")) - { - if (link.From is not SeqElement elem) continue; - SeqElement nxt = elem.NXT; - Thought nextValue = nxt?.VLU; - if (nextValue is null) continue; - - Thought predItem = theUKS.GetOrAddThought("_attn:pred:*", "attention"); - _predRoot.AddLink(_ltPredItem, predItem); - predItem.AddLink(_ltPredFrom, elem); - predItem.AddLink(_ltPredTo, nextValue); - predItem.TimeToLive = TimeSpan.FromSeconds(5); - } } private IEnumerable<(SeqElement source, Thought next)> FindPredictionsFor(Thought focus) @@ -354,23 +308,11 @@ private IEnumerable EnumeratePredictionTargets() private void ClearPredictions() { return; - EnsureLinkTypes(); - foreach (var link in _predRoot.LinksTo.Where(x => x.LinkType == _ltPredItem).ToList()) - { - Thought pred = link.To; - _predRoot.RemoveLink(link); - pred?.Delete(); - } } private static void ReinforcePrediction((SeqElement source, Thought next) pred) { return; - pred.source.Fire(); - var nxtLink = pred.source.LinksToWriteable.FindFirst(x => x.LinkType?.Label == "NXT"); - nxtLink?.Fire(); - pred.source.NXT?.Fire(); - pred.next.Fire(); } private ModuleMentalModel GetMentalModel() diff --git a/BrainSimulator/Modules/ModuleBaseDlg.cs b/BrainSimulator/Modules/ModuleBaseDlg.cs index 52c679e..9aabf24 100644 --- a/BrainSimulator/Modules/ModuleBaseDlg.cs +++ b/BrainSimulator/Modules/ModuleBaseDlg.cs @@ -28,8 +28,6 @@ namespace BrainSimulator.Modules; public class ModuleBaseDlg : Window { public ModuleBase ParentModule; - private DateTime dt; - private DispatcherTimer timer; public int UpdateMS = 100; public Label statusLabel; private bool initializedLayout = false; @@ -45,7 +43,7 @@ private void ModuleBaseDlg_Loaded(object sender, RoutedEventArgs e) initializedLayout = true; // capture original content - UIElement? originalContent = this.Content as UIElement; + UIElement originalContent = this.Content as UIElement; // create outer grid (single row) and overlay bottom bar at the bottom Grid shell = new() @@ -128,7 +126,7 @@ private void ModuleBaseDlg_Loaded(object sender, RoutedEventArgs e) /// Searches for a file with the given name (no path) in the specified root directory /// and all its subdirectories. /// - public static string? FindFile(string rootPath, string fileName) + public static string FindFile(string rootPath, string fileName) { if (!Directory.Exists(rootPath)) throw new DirectoryNotFoundException($"Root path not found: {rootPath}"); @@ -188,24 +186,6 @@ virtual public bool Draw(bool checkDrawTimer) return true; } - public void Timer_Tick(object sender, EventArgs e) - { - timer.Stop(); - if (Application.Current is null) return; - if (this is not null) - Draw(false); - - } - - //this picks up a final draw after 1/4 second - public void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) - { - timer.Stop(); - if (Application.Current is null) return; - if (this is not null) - Draw(false); - } - /// /// Sets a status message at the bottom of the dialog. Seets the background yellow if the color is red or null /// diff --git a/BrainSimulator/Modules/ModuleGPTInfo.cs b/BrainSimulator/Modules/ModuleGPTInfo.cs index 01ff081..7d0caa7 100644 --- a/BrainSimulator/Modules/ModuleGPTInfo.cs +++ b/BrainSimulator/Modules/ModuleGPTInfo.cs @@ -643,7 +643,7 @@ public static void ParseGPTOutput(string textIn, string GPTOutput) } } - public static async Task SolveDuplicates() + public static Task SolveDuplicates() { // Get the UKS. UKS.UKS theUKS = MainWindow.theUKS; @@ -685,6 +685,7 @@ public static async Task SolveDuplicates() ModuleGPTInfoDlg.linkCount++; } + return Task.CompletedTask; } } diff --git a/BrainSimulator/Modules/ModuleGPTInfoDlg.xaml.cs b/BrainSimulator/Modules/ModuleGPTInfoDlg.xaml.cs index 3969a40..36da545 100644 --- a/BrainSimulator/Modules/ModuleGPTInfoDlg.xaml.cs +++ b/BrainSimulator/Modules/ModuleGPTInfoDlg.xaml.cs @@ -215,7 +215,7 @@ public async Task ProcessWordsAsync(List words) if (word == words.Last()) await ModuleGPTInfo.GetChatGPTData(word.Trim()); if (word.Trim() != "") - ModuleGPTInfo.GetChatGPTData(word.Trim()); + _ = ModuleGPTInfo.GetChatGPTData(word.Trim()); } txtOutput.Text = $"Done running! Total word count: {words.Count}. Total link count: {linkCount}. Total error count (not accepted): {errorCount}."; @@ -235,7 +235,7 @@ public async Task ProcessAmbiguityAsync(List words) if (word == words.Last()) await ModuleGPTInfo.DisambiguateTermsFile(word.Trim()); if (word.Trim() != "") - ModuleGPTInfo.DisambiguateTermsFile(word.Trim()); + _ = ModuleGPTInfo.DisambiguateTermsFile(word.Trim()); } txtOutput.Text = $"Done running! Total word count: {words.Count}. Total link count: {linkCount}. Total error count (not accepted): {errorCount}."; @@ -254,7 +254,7 @@ public async Task ProcessParentsAsync(List words) if (word == words.Last()) await ModuleGPTInfo.GetChatGPTParents(word.Trim()); if (word.Trim() != "") - ModuleGPTInfo.GetChatGPTParents(word.Trim()); + _ = ModuleGPTInfo.GetChatGPTParents(word.Trim()); } SetOutputText($"Done processing unknowns! Total word count: {words.Count}. Total link count: {linkCount}. Total error count (not accepted): {errorCount}."); @@ -275,25 +275,26 @@ public async Task verifyAllAsync() if (t == mf.theUKS.AtomicThoughts.Last()) await VerifyAsync(t.Label); else - VerifyAsync(t.Label); + _ = VerifyAsync(t.Label); } SetOutputText($"Done verifying is-a links for reasonableness. Checked {count} links."); } - public async Task VerifyAsync(string label) + public Task VerifyAsync(string label) { ModuleGPTInfo mf = (ModuleGPTInfo)base.ParentModule; if (!label.StartsWith(".")) label = "." + label; UKS.Thought t = mf.theUKS.Labeled(label); - if (t is null) return; + if (t is null) return Task.CompletedTask; foreach (Link r in t.LinksTo) { //if (r.GPTVerified) continue; if (r.LinkType.Label != "has-child") continue; count++; - ModuleGPTInfo.GetChatGPTVerifyParentChild(r.To.Label, t.Label); + _ = ModuleGPTInfo.GetChatGPTVerifyParentChild(r.To.Label, t.Label); } + return Task.CompletedTask; } private void ClearButton_Click(object sender, RoutedEventArgs e) @@ -358,7 +359,7 @@ private void Button_Click(object sender, RoutedEventArgs e) } else if (b.Content.ToString().StartsWith("Verify All")) { - verifyAllAsync(); + _ = verifyAllAsync(); } else if (b.Content.ToString().StartsWith("Add Clauses To All")) { @@ -384,7 +385,7 @@ private void Button_Click(object sender, RoutedEventArgs e) words.Add(thought.Label); } - ProcessParentsAsync(words); + _ = ProcessParentsAsync(words); } } } diff --git a/BrainSimulator/Modules/ModuleLearnMelodyDlg.xaml.cs b/BrainSimulator/Modules/ModuleLearnMelodyDlg.xaml.cs index a7961b5..9db1c2d 100644 --- a/BrainSimulator/Modules/ModuleLearnMelodyDlg.xaml.cs +++ b/BrainSimulator/Modules/ModuleLearnMelodyDlg.xaml.cs @@ -86,7 +86,7 @@ private void TheGrid_SizeChanged(object sender, SizeChangedEventArgs e) Draw(false); } - private void SetStatus(string message, Color? color = null) + private new void SetStatus(string message, Color? color = null) { if (statusLabel != null) { diff --git a/BrainSimulator/Modules/ModuleOnlineInfo.cs b/BrainSimulator/Modules/ModuleOnlineInfo.cs index a0a7924..8fdf15f 100644 --- a/BrainSimulator/Modules/ModuleOnlineInfo.cs +++ b/BrainSimulator/Modules/ModuleOnlineInfo.cs @@ -149,7 +149,7 @@ public void GetCSKGData(string word) } } } - catch (Exception ex) + catch (Exception) { } Debug.WriteLine($"{count.ToString("N0")} entries with {countEN.ToString("N0")} in english"); } @@ -187,6 +187,10 @@ public override bool Equals(object obj) return this == r; return false; } + public override int GetHashCode() + { + return HashCode.Combine(rel, sourceURI, targetURI, fWeight); + } } public void ConceptNetLocal(string word) @@ -323,7 +327,7 @@ public void ConceptNetLocal(string word) } } } - catch (Exception ex) + catch (Exception) { } } @@ -427,7 +431,7 @@ private void ReadConceptNetFile() } } } - catch (Exception ex) + catch (Exception) { } foreach (var w in wordList2) { @@ -624,7 +628,7 @@ public void SetupWordList2(string wordIn = "") } } } - catch (Exception ex) + catch (Exception) { } wordList2 = wordList2.OrderBy(x => x.Item1).ToList(); GetUKS(); @@ -875,10 +879,6 @@ public async Task GetChatGPTResult(string textIn, QueryType qtIn = QueryType.isa { QueryType qType = qtIn; if (altLabel == "") altLabel = textIn; - string prompt; - string apiKey = ConfigurationManager.AppSettings["APIKey"]; - var client = new HttpClient(); - var url = "https://api.openai.com/v1/chat/completions"; string queryText = textIn; textIn = textIn.ToLower(); @@ -931,7 +931,7 @@ public async Task GetChatGPTResult(string textIn, QueryType qtIn = QueryType.isa else Output = answerString; } - catch (Exception ex) + catch (Exception) { } @@ -1020,7 +1020,7 @@ private async Task GetPropertiesFromURL(string itemID, string propName) @"?ps.OPTIONAL{?statement ?pq ?pq_. ?wdpq wikibase:qualifier " + @"?pq .} SERVICE wikibase:label { bd:serviceParam wikibase:language ""en"" }} " + @"ORDER BY ?wd ?statement ?ps_"; - url = urlBegin + Uri.EscapeUriString(url); + url = urlBegin + Uri.EscapeDataString(url); var myClient = new HttpClient(); myClient.DefaultRequestHeaders.Add("User-Agent", "c# program"); var responseURL = await myClient.GetAsync(url); @@ -1074,7 +1074,6 @@ private async Task GetPropertiesFromURL(string itemID, string propName) } docCount++; } - string propString = ""; //TextBoxWiki.Text = ""; for (int i = 0; i < docPropertyValues.Count; i++) { @@ -1098,7 +1097,6 @@ private async Task GetPropertiesFromURL(string itemID, string propName) int docCount = 0; foreach (XmlElement xn in docPropertyValues) { - bool found = false; string[] fn = new string[xn.ChildNodes.Count]; //Thought prop = new Thought(); for (int i = 0; i < xn.ChildNodes.Count; i++) @@ -1122,7 +1120,6 @@ private async Task GetPropertiesFromURL(string itemID, string propName) docCount++; } - string propString = ""; //TextBoxWiki.Text = ""; for (int i = 0; i < docPropertyValues.Count; i++) { diff --git a/BrainSimulator/Modules/ModuleOnlineInfoDlg.xaml.cs b/BrainSimulator/Modules/ModuleOnlineInfoDlg.xaml.cs index eaaccbc..b62e9a9 100644 --- a/BrainSimulator/Modules/ModuleOnlineInfoDlg.xaml.cs +++ b/BrainSimulator/Modules/ModuleOnlineInfoDlg.xaml.cs @@ -84,16 +84,16 @@ private void txtInput_PreviewKeyDown(object sender, KeyEventArgs e) { case "ChatGPT": if (txt.EndsWith("?")) - mcn.GetChatGPTResult(txt, ModuleOnlineInfo.QueryType.general); + _ = mcn.GetChatGPTResult(txt, ModuleOnlineInfo.QueryType.general); else if (txt.StartsWith("list some")) - mcn.GetChatGPTResult(txt.Substring(10), ModuleOnlineInfo.QueryType.list); + _ = mcn.GetChatGPTResult(txt.Substring(10), ModuleOnlineInfo.QueryType.list); else if (txt.StartsWith("count some")) - mcn.GetChatGPTResult(txt.Substring(11), ModuleOnlineInfo.QueryType.listCount); + _ = mcn.GetChatGPTResult(txt.Substring(11), ModuleOnlineInfo.QueryType.listCount); else if (txt.EndsWith("can")) - mcn.GetChatGPTResult(txt.Substring(0,txt.Length-3), ModuleOnlineInfo.QueryType.can); + _ = mcn.GetChatGPTResult(txt.Substring(0,txt.Length-3), ModuleOnlineInfo.QueryType.can); else { - mcn.GetChatGPTResult(txt, qType); + _ = mcn.GetChatGPTResult(txt, qType); //Thread.Sleep(1000); //mcn.GetChatGPTData(txt, ModuleOnlineInfo.QueryType.hasa); //Thread.Sleep(1000); @@ -108,16 +108,16 @@ private void txtInput_PreviewKeyDown(object sender, KeyEventArgs e) mcn.GetConceptNetData(txt); break; case "WikiData": - mcn.GetWikidataData(txt, "subclass of"); + _ = mcn.GetWikidataData(txt, "subclass of"); break; case "Wiktionary": - mcn.GetWiktionaryData(txt); + _ = mcn.GetWiktionaryData(txt); break; case "Free Dictionary": - mcn.GetFreeDictionaryAPIData(txt); + _ = mcn.GetFreeDictionaryAPIData(txt); break; case "Webster's Elementary": - mcn.GetWebstersDictionaryAPIData(txt); + _ = mcn.GetWebstersDictionaryAPIData(txt); break; case "Kid's Definition": mcn.GetKidsDefinition(txt); diff --git a/BrainSimulator/Modules/ModuleSoundIn.cs b/BrainSimulator/Modules/ModuleSoundIn.cs index 1aa9a39..1e06187 100644 --- a/BrainSimulator/Modules/ModuleSoundIn.cs +++ b/BrainSimulator/Modules/ModuleSoundIn.cs @@ -29,9 +29,7 @@ public class ModuleSoundIn : ModuleBase { DateTime lastFiredTime = DateTime.Now; - DateTime? lastNotePressed = null; DateTime lastCadenceTime = DateTime.Now; - List tuneToSearch = null; private readonly Dictionary _pitchs = new(); private const int MinNote = 60; // C4 @@ -42,7 +40,7 @@ public class ModuleSoundIn : ModuleBase private const int MidiPatch = 0; // Acoustic Grand Piano - public static MidiOut? midi; + public static MidiOut midi; public static MidiOut Midi => midi ??= InitMidi(); private static MidiOut InitMidi() diff --git a/BrainSimulator/Modules/ModuleSoundOut.cs b/BrainSimulator/Modules/ModuleSoundOut.cs index 19ef5d4..85ad45b 100644 --- a/BrainSimulator/Modules/ModuleSoundOut.cs +++ b/BrainSimulator/Modules/ModuleSoundOut.cs @@ -29,9 +29,7 @@ public class ModuleSoundOut : ModuleBase { DateTime lastFiredTime = DateTime.Now; - DateTime? lastNotePressed = null; DateTime lastCadenceTime = DateTime.Now; - List tuneToSearch = null; private readonly HashSet _pressedNotes = new(); private readonly Dictionary _pitchs = new(); @@ -43,13 +41,11 @@ public class ModuleSoundOut : ModuleBase private const int MidiPatch = 0; // Acoustic Grand Piano - IEnumerator enumerator = null; //we'll needc multiple enumerators soon - // Add these properties to the ModuleSound class public int Cadence { get; set; } = 100; public int PitchOffset { get; set; } = 0; - public static MidiOut? midi; + public static MidiOut midi; public static MidiOut Midi => midi ??= InitMidi(); private static MidiOut InitMidi() @@ -205,7 +201,7 @@ public async void PlayCMajorTriad(int timetonextMs = 300) Midi.Send(MidiMessage.StopNote(g, 0, channel).RawData); } - public async void PlayNote(int midiNote, bool AddToMentalModel = false, int timetonextMs = 250) + public void PlayNote(int midiNote, bool AddToMentalModel = false, int timetonextMs = 250) { //Debug.WriteLine($"PlayNote: {midiNote} {timetonextMs}"); var listener = MainWindow.theWindow?.activeModules.OfType().FirstOrDefault(); diff --git a/BrainSimulator/Modules/ModuleStressTest.cs b/BrainSimulator/Modules/ModuleStressTest.cs index e047c74..bf1f3d8 100644 --- a/BrainSimulator/Modules/ModuleStressTest.cs +++ b/BrainSimulator/Modules/ModuleStressTest.cs @@ -71,8 +71,6 @@ public static String AddManyTestItems(int count) items.Add(i.ToString()); } - int times = 0; - // Add each item to the UKS. //for (int i = 0; i < items.Count; i++) diff --git a/BrainSimulator/Modules/ModuleStressTestDlg.xaml.cs b/BrainSimulator/Modules/ModuleStressTestDlg.xaml.cs index 3421797..f72378c 100644 --- a/BrainSimulator/Modules/ModuleStressTestDlg.xaml.cs +++ b/BrainSimulator/Modules/ModuleStressTestDlg.xaml.cs @@ -59,7 +59,7 @@ private void SetOutputText(string theText) txtOutput.Text = theText; } - private async void Button_Click(object sender, RoutedEventArgs e) + private void Button_Click(object sender, RoutedEventArgs e) { if (sender is Button btn) { diff --git a/BrainSimulator/Modules/ModuleText.cs b/BrainSimulator/Modules/ModuleText.cs index e6b1864..e0b8fb4 100644 --- a/BrainSimulator/Modules/ModuleText.cs +++ b/BrainSimulator/Modules/ModuleText.cs @@ -189,7 +189,7 @@ public int LoadTextFromFileOld(string filePath) // Incremental file-load state private StreamReader _phraseReader; private string _phraseReaderPath; - public async Task LoadTextFromFile(string filePath, int phrasesPerCall = 500) + public int LoadTextFromFile(string filePath, int phrasesPerCall = 500) { if (phrasesPerCall <= 0) phrasesPerCall = 1; if (!File.Exists(filePath)) @@ -473,78 +473,6 @@ public static int CreateTrigrams() CreateUniversalPatternClasses(); // FindPatterns(); return retVal; - - var theUKS = MainWindow.theUKS; - //int retVal = 0; - - // Ensure type + link types exist - theUKS.GetOrAddThought("trigram", "LanguageElement"); - theUKS.GetOrAddThought("first", "LinkType"); - theUKS.GetOrAddThought("second", "LinkType"); - theUKS.GetOrAddThought("third", "LinkType"); - - // Iterate all existing bigram link-thoughts - foreach (Thought t in ((Thought)"bigram").Children) - { - if (t is not Link lnk) continue; - // Expect: t is a followedBy link from A -> B - Thought a = lnk.From; - Thought b = lnk.To; - - if (a == null || b == null) continue; - - // Find B --followedBy--> C (second bigram) - foreach (Link l in b.LinksTo.Where(x => x.LinkType.Label == "followedBy")) - { - Thought c = l.To; - if (c == null) continue; - if (a.Label == "w:the" && b.Label == "w:baby") - { } - - // Optional: ensure A,B,C occurs somewhere in actual ingested sequences - // This prevents creating trigrams that never appeared. - var results = theUKS.HasSequence(new List { a, b, c }, null); - if (results.Count == 0) - continue; - - // Build a deterministic trigram key (prefer IDs if stable) - string trigramKey = $"tg_{a.Label}_{b.Label}_{c.Label}"; - if (trigramKey == "tg_the_baby_bird") - { } - - Thought tg = theUKS.Labeled(trigramKey); - if (tg is null) - { - tg = theUKS.GetOrAddThought(trigramKey, "trigram"); - - // Link to components (idempotent if AddStatement de-dupes) - theUKS.AddStatement(tg, "first", a); - theUKS.AddStatement(tg, "second", b); - theUKS.AddStatement(tg, "third", c); - - // Set / reinforce trigram weight (use avg or min; min is more conservative) - float w = MathF.Min(t.Weight, l.Weight); // conservative - //float w = 0.5f * (t.Weight + l.Weight); // alternative - - tg.Weight = MathF.Min(tg.Weight, 0.10f * w); // start small but proportional - tg.Weight = results.Count; - retVal++; - } - //else - //{ - // // reinforce existing trigram - // //tg.Weight = MathF.Min(1f, tg.Weight + 0.05f * (1f - tg.Weight)); - // tg.Weight += 1; // simple increment; could also use a weighted average of component weights - //} - - tg.LastFiredTime = DateTime.Now; // swap for UKS ticks later if you add recency - } - } - Thought trigrams = theUKS.Labeled("trigram"); - var topTrigrams = trigrams.Children.OrderByDescending(x => x.Weight).ToList(); - for (int i = 50; i < topTrigrams.Count; i++) - topTrigrams[i].Delete(); - return retVal; } public static void FindPatterns() { diff --git a/BrainSimulator/Modules/ModuleUKSDlg.xaml b/BrainSimulator/Modules/ModuleUKSDlg.xaml index 1c56e74..79d34bf 100644 --- a/BrainSimulator/Modules/ModuleUKSDlg.xaml +++ b/BrainSimulator/Modules/ModuleUKSDlg.xaml @@ -34,7 +34,7 @@ Text="Thought" primitives:TextBoxBase.TextChanged="textBoxRoot_TextChanged" PreviewKeyDown="TextBoxRoot_KeyDown"/> -