diff --git a/NeoCortexApi/AkkaSb.Net/AkkaSb.Net.csproj b/NeoCortexApi/AkkaSb.Net/AkkaSb.Net.csproj index 04061342f..4befe7be1 100644 --- a/NeoCortexApi/AkkaSb.Net/AkkaSb.Net.csproj +++ b/NeoCortexApi/AkkaSb.Net/AkkaSb.Net.csproj @@ -9,6 +9,13 @@ Auto + full + true + + + + full + true diff --git a/NeoCortexApi/DistributedComputeLib/DistributedComputeLib.csproj b/NeoCortexApi/DistributedComputeLib/DistributedComputeLib.csproj index 06f4ccea0..1257f2abf 100644 --- a/NeoCortexApi/DistributedComputeLib/DistributedComputeLib.csproj +++ b/NeoCortexApi/DistributedComputeLib/DistributedComputeLib.csproj @@ -6,6 +6,7 @@ + diff --git a/NeoCortexApi/Documentation/how-does-htmclassifier-work.md b/NeoCortexApi/Documentation/how-does-htmclassifier-work.md new file mode 100644 index 000000000..c4937d57c --- /dev/null +++ b/NeoCortexApi/Documentation/how-does-htmclassifier-work.md @@ -0,0 +1,93 @@ +## HtmClassifier +The HtmClassifier is a helper module that is used to help the process of the next element in the process of learning sequences. +The classifier provides two methods: + +Learn(string key, int[] sdr) + +String Predict(int[] predictiveCells) + +The method learn receives the key string, that represents the sequence and memorizes the SDR for the given key. +Assume, we learn following sequence: +~~~ +1-2-3-4-5-3-5 +~~~ + +In every cycle, the experiment creates the key that represents the sequence in that cycle. For example, the key might look like: + +Cycle 1: '1-2-3-4-5-3-5' , +Cycle 2: '2-3-4-5-3-5-1', +Cycle 3: '3-4-5-3-5-1-2', +etc.. + +During the learning process, the input in every cycle is SDR of cells produced by Temporal Memory algorithm. Because the same SP output (column SDR) for some element (i.e.: ‘3’) will be represented in TM by a different set of cells inside of the same column set. SP generates always (if stable) the same set of active columns for the same element. However, TM does not generate the same set of active cells for the same element. The TM is trying to build the context of the element. +That means ‘3’ followed by ‘2’ produces a different set of active cells than ‘3’ followed by ‘5’. This is why the classifier gets the key in the form shown above. However, developers are free to build a key some other way. + +The following shows the trace output of the learning process. + +~~~ +Col SDR: 3, 451, 515, 532, 534, 972, 976, 979, 981, 984, 986, 997, 1005, 1013, 1014, 1015, 1019, 1020, 1021, 1022, +Cell SDR: *94, 11287, 12895, 13312, 13370, 24302, 24402, 24479, 24542, 24609, 24666, 24925, 25132, 25342, 25354, 25375, 25477, 25513, 25526, 25560,* +Missmatch! Actual value: 14-5-7-6-9-3-4-3-4-3-4-0-1-0-2-3-4-5-6-5-4-3-7-1-9-12-11-12-13-14-11-12 - Predicted value: +Item length: 16 Items: 34 +Predictive cells: 16 530, 855, 1213, 1228, 1339, 1988, 2318, 2925, 13843, 14641, 14961, 15043, 15322, 24538, 24932, 25268, +indx:18 inp/len: 12-13-14-11-12-14-5-7-6-9-3-4-3-4-3-4-0-1-0-2-3-4-5-6-5-4-3-7-1-9-12-11/20 = similarity 2 11532, 11734, 11998, 21032, 22607, 22790, 22888, 23081, 23120, 23361, 23554, 23939, 24011, 24258, 24418, 24486, 24538, 24620, 24673, 24932, +indx:21 inp/len: 11-12-14-5-7-6-9-3-4-3-4-3-4-0-1-0-2-3-4-5-6-5-4-3-7-1-9-12-11-12-13-14/20 = similarity 14 530, 855, 1063, 1153, 1213, 1228, 1339, 1988, 2318, 2925, 13843, 14049, 14188, 14641, 14961, 15043, 15322, 25268, 25365, 25571, +5-7-6-9-3-4-3-4-3-4-0-1-0-2-3-4-5-6-5-4-3-7-1-9-12-11-12-13-14-11-12-14/20 = similarity 14 530, 855, 1063, 1153, 1213, 1228, 1339, 1988, 2318, 2925, 13843, 14037, 14191, 14641, 14961, 15043, 15322, 25268, 25365, 25571, +Linux + + full + true + + + + full + true + + diff --git a/NeoCortexApi/NeoCortexApi.xml b/NeoCortexApi/NeoCortexApi.xml index 5fdde630f..9ab991ad3 100644 --- a/NeoCortexApi/NeoCortexApi.xml +++ b/NeoCortexApi/NeoCortexApi.xml @@ -2067,10 +2067,8 @@ - Used in the {@link TemporalMemory#compute(Connections, int[], boolean)} method - to make pulling values out of the {@link GroupBy2} more readable and named. - Used in the method to make pulling values out of the more readable and named. + Used by Temporal memory algorithm. @@ -2079,6 +2077,20 @@ indicator.Index of slot. + + + Multicore implementation of the Temporal Memory algorithm + + + + + + + + + + + add or update value to particular key diff --git a/NeoCortexApi/NeoCortexApi/HomeostaticPlasticityController.cs b/NeoCortexApi/NeoCortexApi/HomeostaticPlasticityController.cs index d250b6292..09c252870 100644 --- a/NeoCortexApi/NeoCortexApi/HomeostaticPlasticityController.cs +++ b/NeoCortexApi/NeoCortexApi/HomeostaticPlasticityController.cs @@ -79,6 +79,14 @@ protected HomeostaticPlasticityController() } + /// + /// Creates the instance of HomeostaticPlasticityController. + /// + /// The HTM memory. + /// The minimum calls to the Learn method until HPC algorithm is activated. When this number is reached the HPC will disable boosting in SP. + /// Action invoked when the SP status is changed from stable t unstable and vise versa. + /// How many cycles all seen patterns must not change to declare SP as stable. Using smaller numbers might cause frequent status change. + /// Higher numbers ensure more stable SP, but it takes longer time to enter the stable stabe. public HomeostaticPlasticityController(Connections htmMemory, int minCycles, Action onStabilityStatusChanged, int numOfCyclesToWaitOnChange = 50) { this.m_OnStabilityStatusChanged = onStabilityStatusChanged; @@ -407,7 +415,7 @@ public void Serialize(StreamWriter writer) ser.SerializeBegin(nameof(HomeostaticPlasticityController), writer); ser.SerializeValue(this.m_MaxPreviousElements, writer); - // HtmMemory is not serialized here. It is assumed to be serialized in the SP; + // m_HtmMemory is not serialized here. It is assumed to be serialized in the SP; ser.SerializeValue(this.m_Cycle, writer); ser.SerializeValue(this.m_MinCycles, writer); ser.SerializeValue(this.m_RequiredNumOfStableCycles, writer); @@ -425,10 +433,10 @@ public static HomeostaticPlasticityController Deserialize(StreamReader reader) HomeostaticPlasticityController ctrl = new HomeostaticPlasticityController(); HtmSerializer2 ser = new HtmSerializer2(); - ctrl.m_MaxPreviousElements = ser.ReadIntValue(reader); - ctrl.m_MinCycles = ser.ReadIntValue(reader); - ctrl.m_RequiredNumOfStableCycles = ser.ReadIntValue(reader); - //... + //ctrl.m_MaxPreviousElements = ser.ReadIntValue(reader); + //ctrl.m_MinCycles = ser.ReadIntValue(reader); + //ctrl.m_RequiredNumOfStableCycles = ser.ReadIntValue(reader); + ////... return ctrl; diff --git a/NeoCortexApi/NeoCortexApi/NeoCortexApi.csproj b/NeoCortexApi/NeoCortexApi/NeoCortexApi.csproj index 18b8f19d4..d1b80f6aa 100644 --- a/NeoCortexApi/NeoCortexApi/NeoCortexApi.csproj +++ b/NeoCortexApi/NeoCortexApi/NeoCortexApi.csproj @@ -25,6 +25,13 @@ 7.3 TRACE;USE_AKKA + full + true + + + + full + true @@ -61,6 +68,9 @@ + + all + all diff --git a/NeoCortexApi/NeoCortexApi/NeoCortexApi.xml b/NeoCortexApi/NeoCortexApi/NeoCortexApi.xml index 5fdde630f..9ab991ad3 100644 --- a/NeoCortexApi/NeoCortexApi/NeoCortexApi.xml +++ b/NeoCortexApi/NeoCortexApi/NeoCortexApi.xml @@ -2067,10 +2067,8 @@ - Used in the {@link TemporalMemory#compute(Connections, int[], boolean)} method - to make pulling values out of the {@link GroupBy2} more readable and named. - Used in the method to make pulling values out of the more readable and named. + Used by Temporal memory algorithm. @@ -2079,6 +2077,20 @@ indicator.Index of slot. + + + Multicore implementation of the Temporal Memory algorithm + + + + + + + + + + + add or update value to particular key diff --git a/NeoCortexApi/NeoCortexApi/Network/CortexLayer.cs b/NeoCortexApi/NeoCortexApi/Network/CortexLayer.cs index 63a324a77..edf44959b 100644 --- a/NeoCortexApi/NeoCortexApi/Network/CortexLayer.cs +++ b/NeoCortexApi/NeoCortexApi/Network/CortexLayer.cs @@ -84,15 +84,17 @@ public TOUT Compute(TIN input, bool learn) int i = 0; - foreach (var moduleKeyPair in this.HtmModules) + var keys = this.HtmModules.Keys; + + foreach (var key in new List(keys)) { - dynamic module = moduleKeyPair.Value; + dynamic module = this.HtmModules[key]; dynamic moduleInput = (i == 0) ? input : moduleOutput; moduleOutput = module.Compute(moduleInput, learn); - SetResult(moduleKeyPair.Key, moduleOutput); + SetResult(key, moduleOutput); i++; } diff --git a/NeoCortexApi/NeoCortexApi/Network/HtmClassifier.cs b/NeoCortexApi/NeoCortexApi/Network/HtmClassifier.cs index 7476992c9..c6643c3b0 100644 --- a/NeoCortexApi/NeoCortexApi/Network/HtmClassifier.cs +++ b/NeoCortexApi/NeoCortexApi/Network/HtmClassifier.cs @@ -96,12 +96,7 @@ public TIN GetPredictedInputValue(Cell[] predictiveCells) // bool x = false; double maxSameBits = 0; TIN predictedValue = default; - //int[] arr = new int[predictiveCells.Length]; - //for (int i = 0; i < predictiveCells.Length; i++) - //{ - // arr[i] = predictiveCells[i].Index; - //} - + if (predictiveCells.Length != 0) { int indxOfMatchingInp = 0; @@ -109,7 +104,7 @@ public TIN GetPredictedInputValue(Cell[] predictiveCells) int n = 0; List sortedMatches = new List(); - int indx = 0; + var celIndicies = GetCellIndicies(predictiveCells); Debug.WriteLine($"Predictive cells: {celIndicies.Length} \t {Helpers.StringifyVector(celIndicies)}"); @@ -136,7 +131,7 @@ public TIN GetPredictedInputValue(Cell[] predictiveCells) else Debug.WriteLine($"//: IComputeDeco { private static readonly double EPSILON = 0.00001; - private static readonly int cIndexofACTIVE_COLUMNS = 0; + protected static readonly int cIndexofACTIVE_COLUMNS = 0; - private Connections connections; + protected Connections connections; public string Name { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } @@ -70,8 +72,8 @@ public void Init(Connections conn) this.connections.Cells = cells; } - //StreamWriter tmperf1 = new StreamWriter("tm-perf-100000-10cells.csv"); - + //StreamWriter tmperf1 = new StreamWriter("tm-perf-1024-25cells.p.csv"); + /// /// Performs the whole calculation of Temporal memory algorithm. @@ -90,12 +92,12 @@ public ComputeCycle Compute(int[] activeColumns, bool learn) //Stopwatch sw = new Stopwatch(); //sw.Start(); ComputeCycle cycle = ActivateCells(this.connections, activeColumns, learn); - + ActivateDendrites(this.connections, cycle, learn); - + //sw.Stop(); - //tmperf1.WriteLine($"{sw.ElapsedMilliseconds}"); + // tmperf1.WriteLine($"{sw.ElapsedMilliseconds}"); //tmperf1.Flush(); @@ -120,7 +122,8 @@ public ComputeCycle Compute(int[] activeColumns, bool learn) /// /// /// - protected ComputeCycle ActivateCells(Connections conn, int[] activeColumnIndices, bool learn) + + protected virtual ComputeCycle ActivateCells(Connections conn, int[] activeColumnIndices, bool learn) { ComputeCycle cycle = new ComputeCycle { @@ -163,14 +166,14 @@ protected ComputeCycle ActivateCells(Connections conn, int[] activeColumnIndices // Grouping by columns, which have active and matching segments. foreach (var tuple in grouper) { - activeColumnData = activeColumnData.Set(tuple); + activeColumnData.Set(tuple); if (activeColumnData.IsExistAnyActiveCol(cIndexofACTIVE_COLUMNS)) { // If there are some active segments on the column already... if (activeColumnData.ActiveSegments != null && activeColumnData.ActiveSegments.Count > 0) { - Debug.Write("."); + //Debug.Write("."); List cellsOwnersOfActSegs = ActivatePredictedColumn(conn, activeColumnData.ActiveSegments, activeColumnData.MatchingSegments, prevActiveCells, prevWinnerCells, @@ -253,8 +256,9 @@ protected void ActivateDendrites(Connections conn, ComputeCycle cycle, bool lear var activeSegments = new List(); foreach (var item in activity.ActiveSynapses) { - if (item.Value >= conn.HtmConfig.ActivationThreshold) - activeSegments.Add(conn.GetSegmentForFlatIdx(item.Key)); + var seg = conn.GetSegmentForFlatIdx(item.Key); + if(seg != null && item.Value >= conn.HtmConfig.ActivationThreshold) + activeSegments.Add(seg); } // @@ -262,8 +266,9 @@ protected void ActivateDendrites(Connections conn, ComputeCycle cycle, bool lear var matchingSegments = new List(); foreach (var item in activity.PotentialSynapses) { - if (item.Value >= conn.HtmConfig.MinThreshold) - matchingSegments.Add(conn.GetSegmentForFlatIdx(item.Key)); + var seg = conn.GetSegmentForFlatIdx(item.Key); + if (seg != null && item.Value >= conn.HtmConfig.MinThreshold) + matchingSegments.Add(seg); } // @@ -353,7 +358,7 @@ public void Reset(Connections connections) /// /// /// Cells which own active column segments as calculated in the previous step. - private List ActivatePredictedColumn(Connections conn, List columnActiveSegments, + protected List ActivatePredictedColumn(Connections conn, List columnActiveSegments, List matchingSegments, ICollection prevActiveCells, ICollection prevWinnerCells, double permanenceIncrement, double permanenceDecrement, bool learn, IList activeSynapses) { @@ -363,61 +368,56 @@ private List ActivatePredictedColumn(Connections conn, List(conn.GetSynapses(segment))) - foreach (Synapse synapse in new List(segment.Synapses)) + if (!cellsOwnersOfActiveSegments.Contains(segment.ParentCell)) { - // WORKING DRAFT. TM algorithm change. - // The original algorithm adopt all synapses at the segment. - // The new (DRAFT) version adopts only synapses whose presynaptic cells - // are active cells in th eprevious cycle. - if (prevActiveCells.Contains(synapse.getPresynapticCell())) - { - // TODO - // Review this. not only previous cell should be consiered. - // We should rather consider all current list and look if the cell is already in. - segmOwnerCell = segment.ParentCell; - if (segmOwnerCell != previousCell) - { - //activeSynapses.Add(synapse); - cellsOwnersOfActiveSegments.Add(segmOwnerCell); - previousCell = segmOwnerCell; - } - else - { - // for debugging. - } + cellsOwnersOfActiveSegments.Add(segment.ParentCell); + ////DD foreach (Synapse synapse in new List(conn.GetSynapses(segment))) + //foreach (Synapse synapse in new List(segment.Synapses)) + //{ + // // WORKING DRAFT. TM algorithm change. + // // The original algorithm adopt all synapses at the segment. + // // The new (DRAFT) version adopts only synapses whose presynaptic cells + // // are active cells in th eprevious cycle. + // if (prevWinnerCells.Contains(synapse.getPresynapticCell())) + // { + // cellsOwnersOfActiveSegments.Add(segment.ParentCell); + // break; + // //// TODO + // //// Review this. not only previous cell should be consiered. + // //// We should rather consider all current list and look if the cell is already in. + // //segmOwnerCell = segment.ParentCell; + // //if (segmOwnerCell != previousCell) + // //{ + // // //activeSynapses.Add(synapse); + // // cellsOwnersOfActiveSegments.Add(segmOwnerCell); + // // previousCell = segmOwnerCell; + // //} + // //else + // //{ + // // // for debugging. + // //} + // } + //} + } - if (learn) - { - AdaptSegment(conn, segment, prevActiveCells, permanenceIncrement, permanenceDecrement); + if (learn) + { + AdaptSegment(conn, segment, prevActiveCells, permanenceIncrement, permanenceDecrement); - int numActive = conn.LastActivity.PotentialSynapses[segment.SegmentIndex]; - int nGrowDesired = conn.HtmConfig.MaxNewSynapseCount - numActive; + int numActive = conn.LastActivity.PotentialSynapses[segment.SegmentIndex]; + int nGrowDesired = conn.HtmConfig.MaxNewSynapseCount - numActive; - if (nGrowDesired > 0) - { - // Create new synapses on the segment from winner (pre-synaptic cells) cells. - GrowSynapses(conn, prevWinnerCells, segment, conn.HtmConfig.InitialPermanence, - nGrowDesired, conn.HtmConfig.Random); - } - } + if (nGrowDesired > 0) + { + // Create new synapses on the segment from winner (pre-synaptic cells) cells. + GrowSynapses(conn, prevWinnerCells, segment, conn.HtmConfig.InitialPermanence, + nGrowDesired, conn.HtmConfig.Random); + } + else + { + // for debugging. } } - - //if (learn) - //{ - // AdaptSegment(conn, segment, prevActiveCells, permanenceIncrement, permanenceDecrement); - - // int numActive = conn.getLastActivity().PotentialSynapses[segment.getIndex()]; - // int nGrowDesired = conn.HtmConfig.MaxNewSynapseCount - numActive; - - // if (nGrowDesired > 0) - // { - // // Create new synapses on the segment from winner (pre-synaptic cells) cells. - // growSynapses(conn, prevWinnerCells, segment, conn.getInitialPermanence(), - // nGrowDesired, conn.getRandom()); - // } - //} } return cellsOwnersOfActiveSegments; @@ -475,7 +475,7 @@ public BurstingResult BurstColumn(Connections conn, Column column, List 0) { - Debug.Write($"B.({matchingSegments.Count})"); + // Debug.Write($"B.({matchingSegments.Count})"); DistalDendrite maxPotentialSeg = GetSegmentwithHighesPotential(conn, matchingSegments, prevActiveCells); @@ -496,7 +496,7 @@ public BurstingResult BurstColumn(Connections conn, Column column, List activeS List matchingSegments, ICollection prevActiveCells, ICollection prevWinnerCells, double predictedSegmentDecrement) { - + Debug.Write("P"); if (predictedSegmentDecrement > 0) { foreach (DistalDendrite segment in matchingSegments) @@ -686,7 +686,7 @@ public void AdaptSegment(Connections conn, DistalDendrite segment, ICollection synapsesToDestroy = new List(); //DD oreach (Synapse synapse in conn.GetSynapses(segment)) - foreach (Synapse synapse in segment.Synapses) + foreach (Synapse synapse in segment.Synapses) { double permanence = synapse.Permanence; @@ -725,20 +725,16 @@ public void AdaptSegment(Connections conn, DistalDendrite segment, ICollection - /// Used in the method to make pulling values out of the more readable and named. + /// Used by Temporal memory algorithm. /// public class ColumnData { @@ -747,11 +743,9 @@ public class ColumnData public ColumnData() { } - public ColumnData Set(Pair>> t) + public void Set(Pair>> t) { m_Pair = t; - - return this; } public Column Column() { return (Column)m_Pair.Key; } diff --git a/NeoCortexApi/NeoCortexApi/TemporalMemoryMT.cs b/NeoCortexApi/NeoCortexApi/TemporalMemoryMT.cs new file mode 100644 index 000000000..49d1849a2 --- /dev/null +++ b/NeoCortexApi/NeoCortexApi/TemporalMemoryMT.cs @@ -0,0 +1,148 @@ +using NeoCortexApi.Entities; +using NeoCortexApi.Utility; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NeoCortexApi +{ + /// + /// Multicore implementation of the Temporal Memory algorithm + /// + public class TemporalMemoryMT : TemporalMemory + { + /// + /// + /// + /// + /// + /// + /// + protected override ComputeCycle ActivateCells(Connections conn, int[] activeColumnIndices, bool learn) + { + ComputeCycle cycle = new ComputeCycle + { + ActivColumnIndicies = activeColumnIndices + }; + + ConcurrentDictionary cycles = new ConcurrentDictionary(); + + ISet prevActiveCells = conn.ActiveCells; + ISet prevWinnerCells = conn.WinnerCells; + + // The list of active columns. + List activeColumns = new List(); + + foreach (var indx in activeColumnIndices.OrderBy(i => i)) + { + activeColumns.Add(conn.GetColumn(indx)); + } + + Func segToCol = (segment) => + { + var colIndx = ((DistalDendrite)segment).ParentCell.ParentColumnIndex; + var parentCol = this.connections.HtmConfig.Memory.GetColumn(colIndx); + return parentCol; + }; + + Func times1Fnc = x => (Column)x; + + var list = new Pair, Func>[3]; + list[0] = new Pair, Func>(Array.ConvertAll(activeColumns.ToArray(), item => (object)item).ToList(), times1Fnc); + list[1] = new Pair, Func>(Array.ConvertAll(conn.ActiveSegments.ToArray(), item => (object)item).ToList(), segToCol); + list[2] = new Pair, Func>(Array.ConvertAll(conn.MatchingSegments.ToArray(), item => (object)item).ToList(), segToCol); + + GroupBy2 grouper = GroupBy2.Of(list); + + double permanenceIncrement = conn.HtmConfig.PermanenceIncrement; + double permanenceDecrement = conn.HtmConfig.PermanenceDecrement; + + ParallelOptions opts = new ParallelOptions + { + MaxDegreeOfParallelism = Environment.ProcessorCount + }; + + // + // Grouping by columns, which have active and matching segments. + Parallel.ForEach(grouper, opts, (tuple) => + { + ColumnData activeColumnData = new ColumnData(); + + activeColumnData.Set(tuple); + + + if (activeColumnData.IsExistAnyActiveCol(cIndexofACTIVE_COLUMNS)) + { + // If there are some active segments on the column already... + if (activeColumnData.ActiveSegments != null && activeColumnData.ActiveSegments.Count > 0) + { + //Debug.Write("."); + + List cellsOwnersOfActSegs = ActivatePredictedColumn(conn, activeColumnData.ActiveSegments, + activeColumnData.MatchingSegments, prevActiveCells, prevWinnerCells, + permanenceIncrement, permanenceDecrement, learn, cycle.ActiveSynapses); + + ComputeCycle colCycle = new ComputeCycle(); + cycles[tuple.Key.Index] = colCycle; + + foreach (var item in cellsOwnersOfActSegs) + { + colCycle.ActiveCells.Add(item); + colCycle.WinnerCells.Add(item); + } + } + else + { + // + // If no active segments are detected (start of learning) then all cells are activated + // and a random single cell is chosen as a winner. + BurstingResult burstingResult = BurstColumn(conn, activeColumnData.Column(), activeColumnData.MatchingSegments, + prevActiveCells, prevWinnerCells, permanenceIncrement, permanenceDecrement, conn.HtmConfig.Random, + learn); + + // DRAFT. Removing this as unnecessary. + //cycle.ActiveCells.Add(burstingResult.BestCell); + + ComputeCycle colCycle = new ComputeCycle(); + cycles[tuple.Key.Index] = colCycle; + + // + // Here we activate all cells by putting them to list of active cells. + foreach (var item in burstingResult.Cells) + { + colCycle.ActiveCells.Add(item); + } + + //var actSyns = conn.getReceptorSynapses(burstingResult.BestCell).Where(s=>prevActiveCells.Contains(s.SourceCell)); + //foreach (var syn in actSyns) + //{ + // cycle.ActiveSynapses.Add(syn); + //} + + colCycle.WinnerCells.Add((Cell)burstingResult.BestCell); + } + } + else + { + if (learn) + { + PunishPredictedColumn(conn, activeColumnData.ActiveSegments, activeColumnData.MatchingSegments, + prevActiveCells, prevWinnerCells, conn.HtmConfig.PredictedSegmentDecrement); + } + } + }); + + foreach (var colCycle in cycles.Values) + { + cycle.ActiveCells.AddRange(colCycle.ActiveCells); + cycle.WinnerCells.AddRange(colCycle.WinnerCells); + } + + return cycle; + } + + } +} diff --git a/NeoCortexApi/NeoCortexArrayLib/NeoCortexArrayLib.csproj b/NeoCortexApi/NeoCortexArrayLib/NeoCortexArrayLib.csproj index 58d931cd1..7e692a13d 100644 --- a/NeoCortexApi/NeoCortexArrayLib/NeoCortexArrayLib.csproj +++ b/NeoCortexApi/NeoCortexArrayLib/NeoCortexArrayLib.csproj @@ -1,10 +1,18 @@ - + netstandard2.1 + C:\dev\NeoCortexAPI\neocortexapi\NeoCortexApi\NeoCortexArrayLib\NeoCortexArrayLib.xml + full + true + + + + full + true diff --git a/NeoCortexApi/NeoCortexEntities/Entities/Cell.cs b/NeoCortexApi/NeoCortexEntities/Entities/Cell.cs index a3ee5f256..5e2438f25 100644 --- a/NeoCortexApi/NeoCortexEntities/Entities/Cell.cs +++ b/NeoCortexApi/NeoCortexEntities/Entities/Cell.cs @@ -32,6 +32,16 @@ public class Cell : IEquatable, IComparable /// private readonly int m_Hashcode; + /// + /// List of dendrites of the cell. Every dendrite segment is owned bt the cell. + /// + public List DistalDendrites { get; set; } = new List(); + + /// + /// List of receptor synapses that connect this cells as a source cell to the distal dendrit segment owned by some other cell. + /// + public List ReceptorSynapses { get; set; } = new List(); + /// /// Used for testing. /// @@ -59,15 +69,15 @@ public Cell(int parentColumnIndx, int colSeq, int numCellsPerColumn, int cellId, /// - /// Returns the Set of s which have this cell as their source cell. + /// DD Returns the Set of s which have this cell as their source cell. /// /// the connections state of the temporal memory /// create a container for future use if true, if false return an orphaned empty set. /// the Set of s which have this cell as their source cells. - public ISet GetReceptorSynapses(Connections c, bool doLazyCreate = false) - { - return c.GetReceptorSynapses(this, doLazyCreate); - } + //public ISet GetReceptorSynapses(Connections c, bool doLazyCreate = false) + //{ + // return c.GetReceptorSynapses(this, doLazyCreate); + //} /// @@ -76,10 +86,12 @@ public ISet GetReceptorSynapses(Connections c, bool doLazyCreate = fals /// the connections state of the temporal memory /// create a container for future use if true, if false return an orphaned empty set. /// a of this 's s - public List GetSegments(Connections c, bool doLazyCreate = false) - { - return c.GetSegments(this, doLazyCreate); - } + //public List GetSegments(Connections c, bool doLazyCreate = false) + //{ + // //DD + // //return c.GetSegments(this, doLazyCreate); + // return this.DistalDendrites; + //} /// /// Gets the hashcode of the cell. @@ -147,12 +159,53 @@ public void Serialize(StreamWriter writer) ser.SerializeBegin(nameof(Cell), writer); ser.SerializeValue(this.Index, writer); - ser.SerializeValue(this.CellId, writer); + //ser.SerializeValue(this.CellId, writer); ser.SerializeValue(this.ParentColumnIndex, writer); - ser.SerializeValue(this.m_Hashcode, writer); + //ser.SerializeValue(this.m_Hashcode, writer); ser.SerializeEnd(nameof(Cell), writer); } + + public static Cell Deserialize(StreamReader sr) + { + Cell cell = new Cell(); + + HtmSerializer2 ser = new HtmSerializer2(); + string data = sr.ReadToEnd(); + string[] str = data.Split('\n'); + + foreach (string i in str) + { + if( i == "" || i == " BEGIN 'Cell' " || i == " END 'Cell' ") + { continue; } + else + { + string[] istr = i.Split('|'); + int j; + for (j = 0; j < istr.Length; j++) + { + switch (j) + { + case 0: + { + cell.Index = ser.ReadIntValue(istr[j]); + break; + } + case 1: + { + cell.ParentColumnIndex = ser.ReadIntValue(istr[j]); + break; + } + default: + { break; } + } + } + } + } + + return cell; + + } #endregion } } \ No newline at end of file diff --git a/NeoCortexApi/NeoCortexEntities/Entities/Column.cs b/NeoCortexApi/NeoCortexEntities/Entities/Column.cs index b9b26b080..c4c25f2f4 100644 --- a/NeoCortexApi/NeoCortexEntities/Entities/Column.cs +++ b/NeoCortexApi/NeoCortexEntities/Entities/Column.cs @@ -108,7 +108,9 @@ public Cell GetLeastUsedCell(Connections c, Random random) foreach (var cell in Cells) { - int numSegments = cell.GetSegments(c).Count; + //DD + //int numSegments = cell.GetSegments(c).Count; + int numSegments = cell.DistalDendrites.Count; //int numSegments = cell.Segments.Count; if (numSegments < minNumSegments) diff --git a/NeoCortexApi/NeoCortexEntities/Entities/ComputeCycle.cs b/NeoCortexApi/NeoCortexEntities/Entities/ComputeCycle.cs index c413a1dda..e771831d5 100644 --- a/NeoCortexApi/NeoCortexEntities/Entities/ComputeCycle.cs +++ b/NeoCortexApi/NeoCortexEntities/Entities/ComputeCycle.cs @@ -31,22 +31,22 @@ public class ComputeCycle : IEquatable, NeoCortexApi.IModuleData /// if the number of active synapses (permanence higher than connectedPermanence) on that segment is higher than activationThreshold value. /// A Cell is by default in predictive state (depolarized state) if it owns the active dendrite segment. /// - private IList m_PredictiveCells = new List(); + private List m_PredictiveCells = new List(); /// /// Gets the list of active cells. /// - public IList ActiveCells { get; set; } = new List(); + public List ActiveCells { get; set; } = new List(); /// /// Gets the list of winner cells. /// - public IList WinnerCells { get; set; } = new List(); + public List WinnerCells { get; set; } = new List(); /// /// Synapses that create connections to currentlly active cells owners of active segments. /// - public IList ActiveSynapses { get; set; } = new List(); + public List ActiveSynapses { get; set; } = new List(); public int[] ActivColumnIndicies{ get; set; } diff --git a/NeoCortexApi/NeoCortexEntities/Entities/Connections.cs b/NeoCortexApi/NeoCortexEntities/Entities/Connections.cs index 23a943aba..da517fbbb 100644 --- a/NeoCortexApi/NeoCortexEntities/Entities/Connections.cs +++ b/NeoCortexApi/NeoCortexEntities/Entities/Connections.cs @@ -3,12 +3,14 @@ using NeoCortexApi.Types; using NeoCortexApi.Utility; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; +using System.Threading; namespace NeoCortexApi.Entities { @@ -21,7 +23,7 @@ public class Connections public static readonly double EPSILON = 0.00001; - //Internal state + //Internal state private double version = 1.0; /// @@ -39,7 +41,7 @@ public class Connections private double[] m_BoostedmOverlaps; private int[] m_Overlaps; - + /// /// Initialize a tiny random tie breaker. This is used to determine winning /// columns where the overlaps are identical. @@ -65,7 +67,7 @@ public class Connections /// One of all active column cells will be selected as the winner cell. /// public ISet WinnerCells { get => winnerCells; set => winnerCells = value; } - + /// /// All cells. Initialized during initialization of the TemporalMemory. /// @@ -73,7 +75,7 @@ public class Connections private double[] m_BoostFactors; - + private ISet m_ActiveCells = new LinkedHashSet(); private ISet winnerCells = new LinkedHashSet(); private ISet m_PredictiveCells = new LinkedHashSet(); @@ -155,14 +157,14 @@ private set /// /// Reverse mapping from source cell to /// - private Dictionary> m_ReceptorSynapses; + //private Dictionary> m_ReceptorSynapses = new Dictionary>(); /// /// Distal segments of cells. /// - protected Dictionary> m_DistalSegments; + //protected Dictionary> m_DistalSegments = new Dictionary>(); - /// We moved this as a poart of the segment. + /// DD We moved this as a part of the segment. /// /// Synapses, which belong to some distal dentrite segment. /// @@ -203,7 +205,9 @@ private set /// Indexed segments by their global index (can contain nulls). /// Indexed list of distal segments. /// - protected List m_SegmentForFlatIdx = new List(); + //protected List m_SegmentForFlatIdx = new List(); + + protected ConcurrentDictionary m_SegmentForFlatIdx = new ConcurrentDictionary(); /// /// Stores each cycle's most recent activity @@ -213,8 +217,16 @@ private set /// /// The segment creation number. /// - public int NextSegmentOrdinal { get => m_NextSegmentOrdinal; } - + public int NextSegmentOrdinal + { + get + { + lock ("segmentindex") + { + return m_NextSegmentOrdinal; + } + } + } #region Constructors and Initialization @@ -225,6 +237,7 @@ private set /// public Connections() { + // TODO: Remove this when old way of parameter initialization is completely removed. this.m_HtmConfig = new HtmConfig(new int[100], new int[] { 2048 }); } @@ -241,7 +254,7 @@ public Connections(HtmConfig prms) #endregion #region General Methods - + /// /// Returns the specified by the index passed in. /// @@ -815,12 +828,6 @@ public SegmentActivity ComputeActivity(ICollection activeCellsInCurrentCyc Dictionary numOfActiveSynapses = new Dictionary(); Dictionary numOfPotentialSynapses = new Dictionary(); - // Every receptor synapse on active cell, which has permanence over threshold is by default connected. - //int[] numActiveConnectedSynapsesForSegment = new int[nextFlatIdx]; // not needed - - // Every receptor synapse on active cell is active-potential one. - //int[] numActivePotentialSynapsesForSegment = new int[nextFlatIdx]; // not needed - double threshold = connectedPermanence - EPSILON; // @@ -834,7 +841,8 @@ public SegmentActivity ComputeActivity(ICollection activeCellsInCurrentCyc // Receptor synapses are synapses whose source cell (pre-synaptic cell) is the given cell. // Synapse processed here starts with the given 'cell' and points to some other cell that owns some segment in some other column. // The segment owner cell in other column pointed by synapse sourced by this 'cell' is depolirized (in predicting state). - foreach (Synapse synapse in GetReceptorSynapses(cell)) + //DD foreach (Synapse synapse in GetReceptorSynapses(cell)) + foreach (Synapse synapse in cell.ReceptorSynapses) { // Now, we get the segment of the synapse of the pre-synaptic cell. int segFlatIndx = synapse.SegmentIndex; @@ -843,15 +851,12 @@ public SegmentActivity ComputeActivity(ICollection activeCellsInCurrentCyc numOfPotentialSynapses[segFlatIndx] = numOfPotentialSynapses[segFlatIndx] + 1; - //++numActivePotentialSynapsesForSegment[segFlatIndx]; - if (synapse.Permanence > threshold) { if (numOfActiveSynapses.ContainsKey(segFlatIndx) == false) numOfActiveSynapses.Add(segFlatIndx, 0); numOfActiveSynapses[segFlatIndx] = numOfActiveSynapses[segFlatIndx] + 1; - //++numActiveConnectedSynapsesForSegment[segFlatIndx]; } } } @@ -883,7 +888,7 @@ public void StartNewIteration() ///////////////////////////////////////////////////////////////// // Segment (Specifically, Distal Dendrite) Operations // ///////////////////////////////////////////////////////////////// - + #region Segment (Specifically, Distal Dendrite) methods /// /// Adds a new segment on the specified , or reuses an existing one. @@ -897,63 +902,82 @@ public DistalDendrite CreateDistalSegment(Cell segmentParentCell) // least used segments will be destroyed. while (NumSegments(segmentParentCell) >= this.HtmConfig.MaxSegmentsPerCell) { - DestroySegment(LeastRecentlyUsedSegment(segmentParentCell)); + DestroyDistalDendrite(LeastRecentlyUsedSegment(segmentParentCell)); } int flatIdx; - int len; - if ((len = m_FreeFlatIdxs.Count()) > 0) - { - flatIdx = m_FreeFlatIdxs[len - 1]; - m_FreeFlatIdxs.RemoveRange(len - 1, 1); - } - else + + lock ("segmentindex") { - flatIdx = m_NextFlatIdx; - m_SegmentForFlatIdx.Add(null); - ++m_NextFlatIdx; - } + int len; + if ((len = m_FreeFlatIdxs.Count()) > 0) + { + flatIdx = m_FreeFlatIdxs[len - 1]; + m_FreeFlatIdxs.RemoveRange(len - 1, 1); + //if (!m_FreeFlatIdxs.TryRemove(len - 1, out flatIdx)) + // throw new Exception("Object cannot be removed!"); + } + else + { + flatIdx = m_NextFlatIdx; + //m_SegmentForFlatIdx.TryAdd(flatIdx, null); + m_SegmentForFlatIdx[flatIdx] = null; + //m_SegmentForFlatIdx.Add(null); + ++m_NextFlatIdx; + } - int ordinal = m_NextSegmentOrdinal; - ++m_NextSegmentOrdinal; + int ordinal = m_NextSegmentOrdinal; + ++m_NextSegmentOrdinal; - DistalDendrite segment = new DistalDendrite(segmentParentCell, flatIdx, m_TMIteration, ordinal, this.HtmConfig.SynPermConnected, this.HtmConfig.NumInputs); - GetSegments(segmentParentCell, true).Add(segment); - m_SegmentForFlatIdx[flatIdx] = segment; + DistalDendrite segment = new DistalDendrite(segmentParentCell, flatIdx, m_TMIteration, ordinal, this.HtmConfig.SynPermConnected, this.HtmConfig.NumInputs); + segmentParentCell.DistalDendrites.Add(segment); + //GetSegments(segmentParentCell, true).Add(segment); + m_SegmentForFlatIdx[flatIdx] = segment; - return segment; + return segment; + + } } /// /// Destroys a segment /// /// the segment to destroy - public void DestroySegment(DistalDendrite segment) + public void DestroyDistalDendrite(DistalDendrite segment) { - // Remove the synapses from all data structures outside this Segment. - //DD List synapses = GetSynapses(segment); - List synapses = segment.Synapses; - int len = synapses.Count; - - //getSynapses(segment).stream().forEach(s->removeSynapseFromPresynapticMap(s)); - //DD foreach (var s in GetSynapses(segment)) - foreach (var s in segment.Synapses) + lock ("segmentindex") { - RemoveSynapseFromPresynapticMap(s); - } + // Remove the synapses from all data structures outside this Segment. + //DD List synapses = GetSynapses(segment); + List synapses = segment.Synapses; + int len = synapses.Count; + + //getSynapses(segment).stream().forEach(s->removeSynapseFromPresynapticMap(s)); + //DD foreach (var s in GetSynapses(segment)) + foreach (var s in segment.Synapses) + { + RemoveSynapseFromPresynapticMap(s); + } - m_NumSynapses -= len; + lock ("synapses") + { + m_NumSynapses -= len; + } - // Remove the segment from the cell's list. - GetSegments(segment.ParentCell).Remove(segment); + // Remove the segment from the cell's list. + //DD + //GetSegments(segment.ParentCell).Remove(segment); + segment.ParentCell.DistalDendrites.Remove(segment); - // Remove the segment from the map - //DD m_DistalSynapses.Remove(segment); + // Remove the segment from the map + //DD m_DistalSynapses.Remove(segment); - // Free the flatIdx and remove the final reference so the Segment can be - // garbage-collected. - m_FreeFlatIdxs.Add(segment.SegmentIndex); - m_SegmentForFlatIdx[segment.SegmentIndex] = null; + // Free the flatIdx and remove the final reference so the Segment can be + // garbage-collected. + m_FreeFlatIdxs.Add(segment.SegmentIndex); + //m_FreeFlatIdxs[segment.SegmentIndex] = segment.SegmentIndex; + m_SegmentForFlatIdx[segment.SegmentIndex] = null; + } } /// @@ -963,7 +987,10 @@ public void DestroySegment(DistalDendrite segment) /// the least recently activated segment on the specified cell. private DistalDendrite LeastRecentlyUsedSegment(Cell cell) { - List segments = GetSegments(cell, false); + //DD + //List segments = GetSegments(cell, false); + List segments = cell.DistalDendrites; + DistalDendrite minSegment = null; long minIteration = long.MaxValue; @@ -1002,10 +1029,15 @@ public int NumSegments(Cell cell = null) { if (cell != null) { - return GetSegments(cell).Count; + //DD + //return GetSegments(cell).Count; + return cell.DistalDendrites.Count; } - return m_NextFlatIdx - m_FreeFlatIdxs.Count; + lock ("segmentindex") + { + return m_NextFlatIdx - m_FreeFlatIdxs.Count; + } } ///// @@ -1018,33 +1050,34 @@ public int NumSegments(Cell cell = null) // return GetSegments(cell, false); //} + //DD /// /// Returns the mapping of s to their s. /// /// the used as a key. /// create a container for future use if true, if false return an orphaned empty set. /// the mapping of s to their s. - public List GetSegments(Cell cell, bool doLazyCreate = false) - { - if (cell == null) - { - throw new ArgumentException("Cell was null"); - } + //public List GetSegments(Cell cell, bool doLazyCreate = false) + //{ + // if (cell == null) + // { + // throw new ArgumentException("Cell was null"); + // } - if (m_DistalSegments == null) - { - m_DistalSegments = new Dictionary>(); - } + // //if (m_DistalSegments == null) + // //{ + // // m_DistalSegments = new Dictionary>(); + // //} - List retVal; - if ((m_DistalSegments.TryGetValue(cell, out retVal)) == false) - { - if (!doLazyCreate) return new List(); - m_DistalSegments.Add(cell, retVal = new List()); - } + // List retVal; + // if ((m_DistalSegments.TryGetValue(cell, out retVal)) == false) + // { + // if (!doLazyCreate) return new List(); + // m_DistalSegments.Add(cell, retVal = new List()); + // } - return retVal; - } + // return retVal; + //} /// /// Get the segment with the specified flatIdx. @@ -1071,10 +1104,10 @@ public int ColumnIndexForSegment(DistalDendrite segment) /// FOR TEST USE ONLY /// /// - public Dictionary> GetSegmentMapping() - { - return new Dictionary>(m_DistalSegments); - } + //public Dictionary> GetSegmentMapping() + //{ + // return new Dictionary>(m_DistalSegments); + //} /// /// Set/retrieved by the following a compute cycle. @@ -1100,24 +1133,28 @@ public Dictionary> GetSegmentMapping() /// the created . public Synapse CreateSynapse(DistalDendrite segment, Cell presynapticCell, double permanence) { - while (GetNumSynapses(segment) >= this.HtmConfig.MaxSynapsesPerSegment) + while (segment.Synapses.Count >= this.HtmConfig.MaxSynapsesPerSegment) { DestroySynapse(MinPermanenceSynapse(segment), segment); } - Synapse synapse = null; - //DD GetSynapses(segment).Add( + lock ("synapses") + { + Synapse synapse = null; + //DD GetSynapses(segment).Add( segment.Synapses.Add( synapse = new Synapse( presynapticCell, segment.SegmentIndex, m_NextSynapseOrdinal, permanence)); - GetReceptorSynapses(presynapticCell, true).Add(synapse); + presynapticCell.ReceptorSynapses.Add(synapse); + //DD GetReceptorSynapses(presynapticCell, true).Add(synapse); - ++m_NextSynapseOrdinal; + ++m_NextSynapseOrdinal; - ++m_NumSynapses; + ++m_NumSynapses; - return synapse; + return synapse; + } } /// @@ -1127,13 +1164,16 @@ public Synapse CreateSynapse(DistalDendrite segment, Cell presynapticCell, doubl /// public void DestroySynapse(Synapse synapse, DistalDendrite segment) { - --m_NumSynapses; + lock ("synapses") + { + --m_NumSynapses; - RemoveSynapseFromPresynapticMap(synapse); + RemoveSynapseFromPresynapticMap(synapse); - //segment.Synapses.Remove(synapse); - //DD GetSynapses(segment).Remove(synapse); - segment.Synapses.Remove(synapse); + //segment.Synapses.Remove(synapse); + //DD GetSynapses(segment).Remove(synapse); + segment.Synapses.Remove(synapse); + } } /// @@ -1144,14 +1184,17 @@ public void DestroySynapse(Synapse synapse, DistalDendrite segment) /// the synapse to remove public void RemoveSynapseFromPresynapticMap(Synapse synapse) { - LinkedHashSet presynapticSynapses; Cell cell = synapse.getPresynapticCell(); - (presynapticSynapses = GetReceptorSynapses(cell, false)).Remove(synapse); + cell.ReceptorSynapses.Remove(synapse); + //DD + //LinkedHashSet presynapticSynapses; + //Cell cell = synapse.getPresynapticCell(); + //(presynapticSynapses = GetReceptorSynapses(cell, false)).Remove(synapse); - if (presynapticSynapses.Count == 0) - { - m_ReceptorSynapses.Remove(cell); - } + //if (presynapticSynapses.Count == 0) + //{ + // m_ReceptorSynapses.Remove(cell); + //} } /// @@ -1181,22 +1224,17 @@ private Synapse MinPermanenceSynapse(DistalDendrite dd) } - /// - /// Returns the number of s on a given - /// if specified, or the total number if the "optionalSegmentArg" is null. - /// - /// An optional Segment to specify the context of the synapse count. - /// Either the total number of synapses or the number on a specified segment. - public long GetNumSynapses(DistalDendrite optionalSegmentArg = null) - { - if (optionalSegmentArg != null) - { - // DD return GetSynapses(optionalSegmentArg).Count; - return optionalSegmentArg.Synapses.Count; - } - - return m_NumSynapses; - } + ///// + ///// Returns the number of s on a given + ///// if specified, or the total number if the "optionalSegmentArg" is null. + ///// + ///// An optional Segment to specify the context of the synapse count. + ///// Either the total number of synapses or the number on a specified segment. + //public long GetNumSynapses(DistalDendrite optionalSegmentArg) + //{ + // // DD return GetSynapses(optionalSegmentArg).Count; + // return optionalSegmentArg.Synapses.Count; + //} /// @@ -1207,27 +1245,27 @@ public long GetNumSynapses(DistalDendrite optionalSegmentArg = null) /// the used as a key. /// create a container for future use if true, if false return an orphaned empty set. /// the mapping of s to their reverse mapped - public LinkedHashSet GetReceptorSynapses(Cell cell, bool doLazyCreate = false) - { - if (cell == null) - { - throw new ArgumentException("Cell was null"); - } + //public LinkedHashSet GetReceptorSynapses(Cell cell, bool doLazyCreate = false) + //{ + // if (cell == null) + // { + // throw new ArgumentException("Cell was null"); + // } - if (m_ReceptorSynapses == null) - { - m_ReceptorSynapses = new Dictionary>(); - } + // //if (m_ReceptorSynapses == null) + // //{ + // // m_ReceptorSynapses = new Dictionary>(); + // //} - LinkedHashSet retVal = null; - if (m_ReceptorSynapses.TryGetValue(cell, out retVal) == false) - { - if (!doLazyCreate) return new LinkedHashSet(); - m_ReceptorSynapses.Add(cell, retVal = new LinkedHashSet()); - } + // LinkedHashSet retVal = null; + // if (m_ReceptorSynapses.TryGetValue(cell, out retVal) == false) + // { + // if (!doLazyCreate) return new LinkedHashSet(); + // m_ReceptorSynapses.Add(cell, retVal = new LinkedHashSet()); + // } - return retVal; - } + // return retVal; + //} /// /// Returns synapeses of specified dentrite segment. @@ -1256,15 +1294,15 @@ public LinkedHashSet GetReceptorSynapses(Cell cell, bool doLazyCreate = //} - + //DD /// /// For testing only. /// /// Copy of dictionary. - public Dictionary> GetReceptorSynapses() - { - return new Dictionary>(m_ReceptorSynapses); - } + //public Dictionary> GetReceptorSynapses() + //{ + // return new Dictionary>(m_ReceptorSynapses); + //} /// /// Clears the sequence learning state. @@ -1576,9 +1614,9 @@ public override int GetHashCode() result = prime * result + (int)(temp ^ (temp >> 32)); result = prime * result + ((m_PredictiveCells == null) ? 0 : m_PredictiveCells.GetHashCode()); result = prime * result + ((this.HtmConfig.Random == null) ? 0 : this.HtmConfig.Random.GetHashCode()); - result = prime * result + ((m_ReceptorSynapses == null) ? 0 : m_ReceptorSynapses.GetHashCode()); + //result = prime * result + ((m_ReceptorSynapses == null) ? 0 : m_ReceptorSynapses.GetHashCode()); result = prime * result + this.HtmConfig.RandomGenSeed; - result = prime * result + ((m_DistalSegments == null) ? 0 : m_DistalSegments.GetHashCode()); + //result = prime * result + ((m_DistalSegments == null) ? 0 : m_DistalSegments.GetHashCode()); temp = BitConverter.DoubleToInt64Bits(this.HtmConfig.StimulusThreshold); result = prime * result + (int)(temp ^ (temp >> 32)); temp = BitConverter.DoubleToInt64Bits(this.HtmConfig.SynPermActiveInc); @@ -1818,21 +1856,29 @@ public void Serialize(StreamWriter writer) this.connectedCounts2.Serialize(writer); + + ser.SerializeValue(this.Cells, writer); ser.SerializeValue(this.m_BoostFactors, writer); + + + ser.SerializeValue(this.m_ActiveSegments, writer); ser.SerializeValue(this.m_MatchingSegments, writer); this.m_HtmConfig.Serialize(writer); - ser.SerializeValue(this.m_DistalSegments, writer); + + //ser.SerializeValue(this.m_DistalSegments, writer); ser.SerializeValue(this.m_DistalSynapses, writer); ser.SerializeValue(this.m_NextFlatIdx, writer); ser.SerializeValue(this.m_NextSegmentOrdinal, writer); ser.SerializeValue(this.m_NextSynapseOrdinal, writer); ser.SerializeValue(this.m_NumSynapses, writer); ser.SerializeValue(this.m_FreeFlatIdxs, writer); - ser.SerializeValue(this.m_SegmentForFlatIdx, writer); + + // TODO!!! + //ser.SerializeValue(this.m_SegmentForFlatIdx, writer); this.LastActivity.Serialize(writer); diff --git a/NeoCortexApi/NeoCortexEntities/Entities/DistalDendrite.cs b/NeoCortexApi/NeoCortexEntities/Entities/DistalDendrite.cs index 2809b4468..cd8fdf6f5 100644 --- a/NeoCortexApi/NeoCortexEntities/Entities/DistalDendrite.cs +++ b/NeoCortexApi/NeoCortexEntities/Entities/DistalDendrite.cs @@ -58,6 +58,8 @@ public DistalDendrite(Cell parentCell, int flatIdx, long lastUsedIteration, int } + + /// /// Gets all synapses owned by this distal dentrite segment. @@ -156,7 +158,7 @@ public int CompareTo(DistalDendrite other) } #region Serialization - public void Serialize(StreamWriter writer) + public override void Serialize(StreamWriter writer) { HtmSerializer2 ser = new HtmSerializer2(); @@ -171,6 +173,15 @@ public void Serialize(StreamWriter writer) ser.SerializeEnd(nameof(HtmConfig), writer); } #endregion + + //#region Deserialization + //public static DistalDendrite Deserialize(StreamReader sr) + //{ + // //DistalDendrite distal = new DistalDendrite(); + + // //return distal; + //} + //#endregion } } diff --git a/NeoCortexApi/NeoCortexEntities/Entities/HtmConfig.cs b/NeoCortexApi/NeoCortexEntities/Entities/HtmConfig.cs index fcf8ffc2d..4e82ea44a 100644 --- a/NeoCortexApi/NeoCortexEntities/Entities/HtmConfig.cs +++ b/NeoCortexApi/NeoCortexEntities/Entities/HtmConfig.cs @@ -416,6 +416,10 @@ public void Serialize(StreamWriter writer) ser.SerializeValue(this.synPermActiveInc, writer); ser.SerializeValue(this.SynPermConnected, writer); + + + + //Spatial Pooler Variables ser.SerializeValue(this.InhibitionRadius, writer); ser.SerializeValue(this.NumInputs, writer); diff --git a/NeoCortexApi/NeoCortexEntities/Entities/Segment.cs b/NeoCortexApi/NeoCortexEntities/Entities/Segment.cs index 84deaad9f..05cb0f9bc 100644 --- a/NeoCortexApi/NeoCortexEntities/Entities/Segment.cs +++ b/NeoCortexApi/NeoCortexEntities/Entities/Segment.cs @@ -134,15 +134,16 @@ public override string ToString() { return $"Seg: {this.SegmentIndex}"; } + #region Serialization - public void Serialize(StreamWriter writer) + public virtual void Serialize(StreamWriter writer) { HtmSerializer2 ser = new HtmSerializer2(); ser.SerializeBegin(nameof(HtmConfig), writer); ser.SerializeValue(this.SegmentIndex, writer); - ser.SerializeValue(this.boxedIndex, writer); + this.boxedIndex.Serialize(writer); ser.SerializeValue(this.Synapses, writer); ser.SerializeValue(this.SynapsePermConnected, writer); ser.SerializeValue(this.NumInputs, writer); diff --git a/NeoCortexApi/NeoCortexEntities/Entities/Synapse.cs b/NeoCortexApi/NeoCortexEntities/Entities/Synapse.cs index 1d3f6ca22..adab0daaf 100644 --- a/NeoCortexApi/NeoCortexEntities/Entities/Synapse.cs +++ b/NeoCortexApi/NeoCortexEntities/Entities/Synapse.cs @@ -302,7 +302,7 @@ public void Serialize(StreamWriter writer) this.SourceCell.Serialize(writer); ser.SerializeValue(this.SegmentIndex, writer); ser.SerializeValue(this.SynapseIndex, writer); - ser.SerializeValue(this.BoxedIndex, writer); + this.BoxedIndex.Serialize(writer); ser.SerializeValue(this.InputIndex, writer); ser.SerializeValue(this.Permanence, writer); ser.SerializeValue(this.IsDestroyed, writer); diff --git a/NeoCortexApi/NeoCortexEntities/HtmSerializer2.cs b/NeoCortexApi/NeoCortexEntities/HtmSerializer2.cs index 1843efc46..bc97b181d 100644 --- a/NeoCortexApi/NeoCortexEntities/HtmSerializer2.cs +++ b/NeoCortexApi/NeoCortexEntities/HtmSerializer2.cs @@ -18,11 +18,11 @@ namespace NeoCortexApi public class HtmSerializer2 { //SP - const string valueDelimiter = " "; + const char valueDelimiter = ' '; - const string typeDelimiter = " "; + const char typeDelimiter = ' '; - const string parameterDelimiter = "|"; + const char parameterDelimiter = '|'; /// /// Serializes the begin marker of the type. @@ -40,7 +40,14 @@ public void SerializeBegin(String typeName, StreamWriter sw) sw.WriteLine(); } + public String ReadBegin(StreamReader sr) + { + string val = sr.ReadLine(); + val += sr.ReadLine(); + val += sr.ReadLine(); + return val; + } /// /// Serialize the end marker of the type. /// @@ -53,6 +60,14 @@ public void SerializeEnd(String typeName, StreamWriter sw) sw.WriteLine(); } + public String ReadEnd(StreamReader sr) + { + string val = sr.ReadLine(); + val += sr.ReadLine(); + val += sr.ReadLine(); + return val; + + } /// /// Serialize the property of type Double. /// @@ -63,6 +78,17 @@ public void SerializeValue(double val, StreamWriter sw) sw.Write(valueDelimiter); sw.Write(parameterDelimiter); } + + + /// + /// Deserialize the property of type Double. + /// + public Double ReadDoubleValue(StreamReader sr) + { + string value = sr.ReadLine(); + Double val = Convert.ToDouble(value); + return val; + } /// /// Serialize the property of type String. @@ -74,7 +100,8 @@ public void SerializeValue(String val, StreamWriter sw) sw.Write(valueDelimiter); sw.Write(parameterDelimiter); } - + + /// /// Serialize the property of type Int. /// @@ -85,6 +112,19 @@ public void SerializeValue(int val, StreamWriter sw) sw.Write(valueDelimiter); sw.Write(parameterDelimiter); } + + /// + /// TODO + /// + /// + /// + public int ReadIntValue(String reader) + { + reader = reader.Trim(); + int val = Convert.ToInt16(reader); + return val; + + } /// /// Serialize the property of type long. /// @@ -287,25 +327,8 @@ public void SerializeValue(Dictionary> keyValues, Str } sw.Write(parameterDelimiter); } - /// - /// TODO - /// - /// - /// - public int ReadIntValue(StreamReader reader) - { - throw new NotImplementedException(); - } - /// - /// Deserialize the property of type Double. - /// - public Double ReadDoubleValue(StreamReader sr) - { - Double val = 0.0; - string value = sr.ReadToEnd(); - val = Convert.ToDouble(value); - return val; - } + + /// /// Deserialize the property of type String. /// diff --git a/NeoCortexApi/NeoCortexEntities/NeoCortexEntities.csproj b/NeoCortexApi/NeoCortexEntities/NeoCortexEntities.csproj index eeef312e6..7be3b2bae 100644 --- a/NeoCortexApi/NeoCortexEntities/NeoCortexEntities.csproj +++ b/NeoCortexApi/NeoCortexEntities/NeoCortexEntities.csproj @@ -8,12 +8,18 @@ TRACE;USE_AKKA + C:\dev\NeoCortexAPI\neocortexapi\NeoCortexApi\NeoCortexEntities\NeoCortexEntities.xml + full + true TRACE;USE_AKKA + full + true + diff --git a/NeoCortexApi/NeoCortexEntities/Types/Integer.cs b/NeoCortexApi/NeoCortexEntities/Types/Integer.cs index 78a1ea3bf..e5acb59e8 100644 --- a/NeoCortexApi/NeoCortexEntities/Types/Integer.cs +++ b/NeoCortexApi/NeoCortexEntities/Types/Integer.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. using System; using System.Collections.Generic; +using System.IO; using System.Text; namespace NeoCortexApi.Entities @@ -51,5 +52,60 @@ public int CompareTo(Integer other) { return Comparer.Default.Compare(this.Value, other.Value); } + + #region Serialization + public void Serialize(StreamWriter writer) + { + HtmSerializer2 ser = new HtmSerializer2(); + + ser.SerializeBegin(nameof(Integer), writer); + + ser.SerializeValue(this.Value,writer); + //ser.SerializeValue(Integer.MaxValue, writer); + //ser.SerializeValue(Integer.MinValue, writer); + + ser.SerializeEnd(nameof(Integer), writer); + } + #endregion + + #region Deserialization + public static Integer Deserialize(StreamReader sr) + { + Integer inte = new Integer(); + + HtmSerializer2 ser = new HtmSerializer2(); + string data = sr.ReadToEnd(); + string[] str = data.Split('\n'); + + foreach (string i in str) + { + if (i == "" || i == " BEGIN 'Integer' " || i == " END 'Integer' ") + { continue; } + else + { + string[] istr = i.Split('|'); + int j; + for (j = 0; j < istr.Length; j++) + { + switch (j) + { + case 0: + { + inte.Value = ser.ReadIntValue(istr[j]); + break; + } + default: + { break; } + + } + } + } + } + + return inte; + + } + + #endregion + } } -} diff --git a/NeoCortexApi/NeoCortexUtils/NeoCortexUtils.csproj b/NeoCortexApi/NeoCortexUtils/NeoCortexUtils.csproj index bbc03f0b2..503f5041e 100644 --- a/NeoCortexApi/NeoCortexUtils/NeoCortexUtils.csproj +++ b/NeoCortexApi/NeoCortexUtils/NeoCortexUtils.csproj @@ -5,6 +5,14 @@ + C:\dev\NeoCortexAPI\neocortexapi\NeoCortexApi\NeoCortexUtils\NeoCortexUtils.xml + full + true + + + + full + true diff --git a/NeoCortexApi/Samples/NeoCortexApiSample/NeoCortexApiSample.csproj b/NeoCortexApi/Samples/NeoCortexApiSample/NeoCortexApiSample.csproj index 9c5700835..0b0780bb8 100644 --- a/NeoCortexApi/Samples/NeoCortexApiSample/NeoCortexApiSample.csproj +++ b/NeoCortexApi/Samples/NeoCortexApiSample/NeoCortexApiSample.csproj @@ -6,6 +6,16 @@ + + full + true + + + + full + true + + diff --git a/NeoCortexApi/Samples/NeoCortexApiSample/SequenceLearning.cs b/NeoCortexApi/Samples/NeoCortexApiSample/SequenceLearning.cs index 0810aebb2..f5977c28c 100644 --- a/NeoCortexApi/Samples/NeoCortexApiSample/SequenceLearning.cs +++ b/NeoCortexApi/Samples/NeoCortexApiSample/SequenceLearning.cs @@ -52,7 +52,7 @@ public void Run() // Used by punishing of segments. PredictedSegmentDecrement = 0.1 }; - + double max = 20; Dictionary settings = new Dictionary() @@ -90,15 +90,17 @@ private void RunExperiment(int inputBits, HtmConfig cfg, EncoderBase encoder, Li var mem = new Connections(cfg); - bool isInStableState; + bool isInStableState = false; HtmClassifier cls = new HtmClassifier(); var numInputs = inputValues.Distinct().ToList().Count; + CortexLayer layer1 = new CortexLayer("L1"); + TemporalMemory tm = new TemporalMemory(); - HomeostaticPlasticityController hpa = new HomeostaticPlasticityController(mem, numInputs * 55, (isStable, numPatterns, actColAvg, seenInputs) => + HomeostaticPlasticityController hpa = new HomeostaticPlasticityController(mem, numInputs * 150, (isStable, numPatterns, actColAvg, seenInputs) => { if (isStable) // Event should be fired when entering the stable state. @@ -107,26 +109,26 @@ private void RunExperiment(int inputBits, HtmConfig cfg, EncoderBase encoder, Li // Ideal SP should never enter unstable state after stable state. Debug.WriteLine($"INSTABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}"); - isInStableState = true; + // We are not learning in instable state. + learn = isInStableState = isStable; + + //if (isStable && layer1.HtmModules.ContainsKey("tm") == false) + // layer1.HtmModules.Add("tm", tm); // Clear all learned patterns in the classifier. cls.ClearState(); // Clear active and predictive cells. - tm.Reset(mem); - - }, numOfCyclesToWaitOnChange: 25); + //tm.Reset(mem); + }, numOfCyclesToWaitOnChange: 50); SpatialPoolerMT sp = new SpatialPoolerMT(hpa); sp.Init(mem); tm.Init(mem); - CortexLayer layer1 = new CortexLayer("L1"); - layer1.HtmModules.Add("encoder", encoder); layer1.HtmModules.Add("sp", sp); - layer1.HtmModules.Add("tm", tm); double[] inputs = inputValues.ToArray(); int[] prevActiveCols = new int[0]; @@ -144,68 +146,118 @@ private void RunExperiment(int inputBits, HtmConfig cfg, EncoderBase encoder, Li activeColumnsLst.Add(input, new List>()); } - int maxCycles = 100;// 3500; + int maxCycles = 3500; int maxPrevInputs = inputValues.Count - 1; List previousInputs = new List(); previousInputs.Add("-1.0"); // - // Now training with SP+TM. SP is pretrained on the given input pattern. + // Training SP to get stable. New-born stage. + // + for (int i = 0; i < maxCycles; i++) { matches = 0; cycle++; - Debug.WriteLine($"-------------- Cycle {cycle} ---------------"); + Debug.WriteLine($"-------------- Newborn Cycle {cycle} ---------------"); foreach (var input in inputs) { - Debug.WriteLine($"-------------- {input} ---------------"); - - var lyrOut = layer1.Compute(input, learn) as ComputeCycle; - - var activeColumns = layer1.GetResult("sp") as int[]; + Debug.WriteLine($" -- {input} --"); - activeColumnsLst[input].Add(activeColumns.ToList()); + var lyrOut = layer1.Compute(input, learn); - previousInputs.Add(input.ToString()); - if (previousInputs.Count > (maxPrevInputs + 1)) - previousInputs.RemoveAt(0); + if (isInStableState) + break; + } - string key = GetKey(previousInputs, input); + if (isInStableState) + break; + } - //cls.Learn(GetKey(prevInput, input), lyrOut.ActiveCells.ToArray()); - cls.Learn(key, lyrOut.ActiveCells.ToArray()); + layer1.HtmModules.Add("tm", tm); - if (learn == false) - Debug.WriteLine($"Inference mode"); + // + // Now training with SP+TM. SP is pretrained on the given input pattern set. + for (int i = 0; i < maxCycles; i++) + { + matches = 0; - Debug.WriteLine($"Col SDR: {Helpers.StringifyVector(lyrOut.ActivColumnIndicies)}"); - Debug.WriteLine($"Cell SDR: {Helpers.StringifyVector(lyrOut.ActiveCells.Select(c => c.Index).ToArray())}"); + cycle++; - if (key == lastPredictedValue) - { - matches++; - Debug.WriteLine($"Match. Actual value: {key} - Predicted value: {lastPredictedValue}"); - } - else - Debug.WriteLine($"Missmatch! Actual value: {key} - Predicted value: {lastPredictedValue}"); + Debug.WriteLine($"-------------- Cycle {cycle} ---------------"); - if (lyrOut.PredictiveCells.Count > 0) - { - var predictedInputValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray()); + foreach (var input in inputs) + { + Debug.WriteLine($"-------------- {input} ---------------"); - Debug.WriteLine($"Current Input: {input} \t| Predicted Input: {predictedInputValue}"); + var lyrOut = layer1.Compute(input, learn) as ComputeCycle; - lastPredictedValue = predictedInputValue; - } - else + // lyrOut is null when the TM is added to the layer inside of HPC callback by entering of the stable state. + if (isInStableState && lyrOut != null) { - Debug.WriteLine($"NO CELLS PREDICTED for next cycle."); - lastPredictedValue = String.Empty; + var activeColumns = layer1.GetResult("sp") as int[]; + + //layer2.Compute(lyrOut.WinnerCells, true); + activeColumnsLst[input].Add(activeColumns.ToList()); + + previousInputs.Add(input.ToString()); + if (previousInputs.Count > (maxPrevInputs + 1)) + previousInputs.RemoveAt(0); + + // In the pretrained SP with HPC, the TM will quickly learn cells for patterns + // In that case the starting sequence 4-5-6 might have the sam SDR as 1-2-3-4-5-6, + // Which will result in returning of 4-5-6 instead of 1-2-3-4-5-6. + // HtmClassifier allways return the first matching sequence. Because 4-5-6 will be as first + // memorized, it will match as the first one. + if (previousInputs.Count < maxPrevInputs) + continue; + + string key = GetKey(previousInputs, input); + + List actCells; + + if (lyrOut.ActiveCells.Count == lyrOut.WinnerCells.Count) + { + actCells = lyrOut.ActiveCells; + } + else + { + actCells = lyrOut.WinnerCells; + } + + cls.Learn(key, actCells.ToArray()); + + if (learn == false) + Debug.WriteLine($"Inference mode"); + + Debug.WriteLine($"Col SDR: {Helpers.StringifyVector(lyrOut.ActivColumnIndicies)}"); + Debug.WriteLine($"Cell SDR: {Helpers.StringifyVector(actCells.Select(c => c.Index).ToArray())}"); + + if (key == lastPredictedValue) + { + matches++; + Debug.WriteLine($"Match. Actual value: {key} - Predicted value: {lastPredictedValue}"); + } + else + Debug.WriteLine($"Missmatch! Actual value: {key} - Predicted value: {lastPredictedValue}"); + + if (lyrOut.PredictiveCells.Count > 0) + { + var predictedInputValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray()); + + Debug.WriteLine($"Current Input: {input} \t| Predicted Input: {predictedInputValue}"); + + lastPredictedValue = predictedInputValue; + } + else + { + Debug.WriteLine($"NO CELLS PREDICTED for next cycle."); + lastPredictedValue = String.Empty; + } } - } // The brain does not do that this way, so we don't use it. diff --git a/NeoCortexApi/UnitTestsProject/SequenceLearningExperiments/MuscNotesExperiment.cs b/NeoCortexApi/UnitTestsProject/SequenceLearningExperiments/MuscNotesExperiment.cs index 217d02803..e0ac657ec 100644 --- a/NeoCortexApi/UnitTestsProject/SequenceLearningExperiments/MuscNotesExperiment.cs +++ b/NeoCortexApi/UnitTestsProject/SequenceLearningExperiments/MuscNotesExperiment.cs @@ -163,7 +163,7 @@ private void RunExperiment(int inputBits, Parameters p, EncoderBase encoder, Lis p.apply(mem); - bool isInStableState; + bool isInStableState = false; //HtmClassifier cls = new HtmClassifier(); HtmClassifier cls = new HtmClassifier(); @@ -246,14 +246,25 @@ private void RunExperiment(int inputBits, Parameters p, EncoderBase encoder, Lis string key = GetKey(previousInputs, input); - //cls.Learn(GetKey(prevInput, input), lyrOut.ActiveCells.ToArray()); - cls.Learn(key, lyrOut.ActiveCells.ToArray()); + + List actCells; + + if (lyrOut.ActiveCells.Count == lyrOut.WinnerCells.Count) + { + actCells = lyrOut.ActiveCells; + } + else + { + actCells = lyrOut.WinnerCells; + } + + cls.Learn(key, actCells.ToArray()); if (learn == false) Debug.WriteLine($"Inference mode"); Debug.WriteLine($"Col SDR: {Helpers.StringifyVector(lyrOut.ActivColumnIndicies)}"); - Debug.WriteLine($"Cell SDR: {Helpers.StringifyVector(lyrOut.ActiveCells.Select(c => c.Index).ToArray())}"); + Debug.WriteLine($"Cell SDR: {Helpers.StringifyVector(actCells.Select(c => c.Index).ToArray())}"); if (key == lastPredictedValue) { diff --git a/NeoCortexApi/UnitTestsProject/SerializationTests.cs b/NeoCortexApi/UnitTestsProject/SerializationTests.cs index 3dea71f12..ece3af78e 100644 --- a/NeoCortexApi/UnitTestsProject/SerializationTests.cs +++ b/NeoCortexApi/UnitTestsProject/SerializationTests.cs @@ -298,16 +298,61 @@ public void SerializeValueToFileTest() using (StreamWriter sw = new StreamWriter("ser.txt")) { + var sp1 = new SpatialPooler(); HtmSerializer2 ser = new HtmSerializer2(); ser.SerializeBegin("UnitTest", sw); Dictionary myDictionary = new Dictionary(); - myDictionary.Add("Sunday",new int[] { 1, 2, 3, 6}); + myDictionary.Add("Sunday", new int[] { 1, 2, 3, 6 }); myDictionary.Add("Monday", new int[] { 2, 4, 5 }); - ser.SerializeValue(myDictionary,sw); + ser.SerializeValue(myDictionary, sw); ser.SerializeEnd("UnitTest", sw); } } + [TestMethod] + [TestCategory("Serialization")] + public void SerializeCell() + { + + HtmSerializer2 ser = new HtmSerializer2(); + Cell cell = new Cell(); + Cell cell1; + using (StreamWriter sw = new StreamWriter("ser.txt")) + { + cell.Serialize(sw); + } + using (StreamReader sr = new StreamReader("ser.txt")) + + { + cell1 = Cell.Deserialize(sr); + } + + Assert.IsTrue(cell1.Equals(cell)); + + } + + [TestMethod] + [TestCategory("Serialization")] + public void SerializeInteger() + { + + HtmSerializer2 ser = new HtmSerializer2(); + Integer inte = new Integer(); + Integer inte1; + using (StreamWriter sw = new StreamWriter("ser.txt")) + { + inte.Serialize(sw); + } + using (StreamReader sr = new StreamReader("ser.txt")) + + { + inte1 = Integer.Deserialize(sr); + } + + Assert.IsTrue(inte1.Equals(inte)); + + } + private static Parameters GetDefaultParams() { ThreadSafeRandom rnd = new ThreadSafeRandom(42); @@ -333,6 +378,7 @@ private static Parameters GetDefaultParams() return parameters; } + [TestMethod] [TestCategory("LongRunning")] public void SerializationTest1() diff --git a/NeoCortexApi/UnitTestsProject/TemporalMemoryTests.cs b/NeoCortexApi/UnitTestsProject/TemporalMemoryTests.cs index 3f7d4ae76..8922e0302 100644 --- a/NeoCortexApi/UnitTestsProject/TemporalMemoryTests.cs +++ b/NeoCortexApi/UnitTestsProject/TemporalMemoryTests.cs @@ -415,7 +415,9 @@ public void TestNewSegmentAddSynapsesToSubsetOfWinnerCells() List winnerCells = new List(cc.WinnerCells); Assert.AreEqual(1, winnerCells.Count); - List segments = winnerCells[0].GetSegments(cn); + //DD + //List segments = winnerCells[0].GetSegments(cn); + List segments = winnerCells[0].DistalDendrites; //List segments = winnerCells[0].Segments; Assert.AreEqual(1, segments.Count); @@ -451,7 +453,11 @@ public void TestNewSegmentAddSynapsesToAllWinnerCells() List winnerCells = new List(cc.WinnerCells); Assert.AreEqual(1, winnerCells.Count); - List segments = winnerCells[0].GetSegments(cn); + + //DD + //List segments = winnerCells[0].GetSegments(cn); + List segments = winnerCells[0].DistalDendrites; + //List segments = winnerCells[0].Segments; Assert.AreEqual(1, segments.Count); List synapses = segments[0].GetAllSynapses(cn); @@ -621,7 +627,7 @@ public void TestDestroyWeakSynapseOnWrongPrediction() tm.Compute(previousActiveColumns, true); tm.Compute(activeColumns, true); - Assert.AreEqual(3, cn.GetNumSynapses(activeSegment)); + Assert.AreEqual(3, activeSegment.Synapses.Count); } [TestMethod] @@ -651,7 +657,7 @@ public void TestDestroyWeakSynapseOnActiveReinforce() tm.Compute(previousActiveColumns, true); tm.Compute(activeColumns, true); - Assert.AreEqual(3, cn.GetNumSynapses(activeSegment)); + Assert.AreEqual(3, activeSegment.Synapses.Count); } [TestMethod] @@ -725,13 +731,19 @@ public void TestRecycleLeastRecentlyActiveSegmentToMakeRoomForNewSegment() tm.Compute(prevActiveColumns1, true); tm.Compute(activeColumns, true); - Assert.AreEqual(1, cn.GetSegments(cell9).Count); - DistalDendrite oldestSegment = cn.GetSegments(cell9)[0]; + //DD + //Assert.AreEqual(1, cn.GetSegments(cell9).Count); + Assert.AreEqual(1, cell9.DistalDendrites.Count); + //DD + //DistalDendrite oldestSegment = cn.GetSegments(cell9)[0]; + DistalDendrite oldestSegment = cell9.DistalDendrites[0]; tm.Reset(cn); tm.Compute(prevActiveColumns2, true); tm.Compute(activeColumns, true); - Assert.AreEqual(2, cn.GetSegments(cell9).Count); + //DD + //Assert.AreEqual(2, cn.GetSegments(cell9).Count); + Assert.AreEqual(2, cell9.DistalDendrites.Count); //Set oldPresynaptic = cn.getSynapses(oldestSegment) // .stream() @@ -744,12 +756,17 @@ public void TestRecycleLeastRecentlyActiveSegmentToMakeRoomForNewSegment() tm.Reset(cn); tm.Compute(prevActiveColumns3, true); tm.Compute(activeColumns, true); - Assert.AreEqual(2, cn.GetSegments(cell9).Count); + + //DD + //Assert.AreEqual(2, cn.GetSegments(cell9).Count); + Assert.AreEqual(2, cell9.DistalDendrites.Count); // Verify none of the segments are connected to the cells the old // segment was connected to. - foreach (DistalDendrite segment in cn.GetSegments(cell9)) + //DD + //foreach (DistalDendrite segment in cn.GetSegments(cell9)) + foreach (DistalDendrite segment in cell9.DistalDendrites) { //Set newPresynaptic = cn.getSynapses(segment) // .stream() @@ -867,13 +884,17 @@ public void TestAddSegmentToCellWithFewestSegments() Assert.AreEqual(3, cn.NumSegments()); Assert.AreEqual(1, cn.NumSegments(cn.GetCell(0))); Assert.AreEqual(1, cn.NumSegments(cn.GetCell(3))); - Assert.AreEqual(1, cn.GetNumSynapses(segment1)); - Assert.AreEqual(1, cn.GetNumSynapses(segment2)); + Assert.AreEqual(1, segment1.Synapses.Count); + Assert.AreEqual(1, segment2.Synapses.Count); - List segments = new List(cn.GetSegments(cn.GetCell(1))); + //DD + //List segments = new List(cn.GetSegments(cn.GetCell(1))); + List segments = new List(cn.GetCell(1).DistalDendrites); if (segments.Count == 0) { - List segments2 = cn.GetSegments(cn.GetCell(2)); + //DD + //List segments2 = cn.GetSegments(cn.GetCell(2)); + List segments2 = cn.GetCell(2).DistalDendrites; Assert.IsFalse(segments2.Count == 0); grewOnCell2 = true; segments.AddRange(segments2); @@ -936,18 +957,56 @@ public void TestConnectionsNeverChangeWhenLearningDisabled() cn.CreateSynapse(wrongMatchingSegment, prevActiveCells[1], 0.5); cn.CreateSynapse(wrongMatchingSegment, prevInactiveCell, 0.5); - var r = deepCopyPlain(cn.GetReceptorSynapses().Values.First().First()); - var synMapBefore = deepCopyPlain>>(cn.GetReceptorSynapses()); - var segMapBefore = deepCopyPlain>>(cn.GetSegmentMapping()); + //var r = deepCopyPlain(cn.GetReceptorSynapses().Values.First().First()); + //var synMapBefore = deepCopyPlain>>(cn.GetReceptorSynapses()); + //var segMapBefore = deepCopyPlain>>(cn.GetSegmentMapping()); + var actCellsBefore = cn.ActiveCells; + var winCellsBefore = cn.WinnerCells; + + int prevPresynSum = 0; + int segSynSum = 0; + + GetSynSums(cn, out prevPresynSum, out segSynSum); tm.Compute(prevActiveColumns, false); tm.Compute(activeColumns, false); - Assert.IsTrue(synMapBefore != cn.GetReceptorSynapses()); - Assert.IsTrue(synMapBefore.Keys.SequenceEqual(cn.GetReceptorSynapses().Keys)); + int afterPresynSum = 0; + int afterSegSynSum = 0; + + GetSynSums(cn, out afterPresynSum, out afterSegSynSum); + + //DD + //Assert.IsTrue(synMapBefore != cn.GetReceptorSynapses()); + //Assert.IsTrue(synMapBefore.Keys.SequenceEqual(cn.GetReceptorSynapses().Keys)); + Assert.IsTrue(prevPresynSum == afterPresynSum); + Assert.IsTrue(segSynSum == afterSegSynSum); + + cn.ActiveCells.SequenceEqual(actCellsBefore); + cn.WinnerCells.SequenceEqual(winCellsBefore); + //DD + //Assert.IsTrue(segMapBefore != cn.GetSegmentMapping()); + //Assert.IsTrue(segMapBefore.Keys.SequenceEqual(cn.GetSegmentMapping().Keys)); + } + + private void GetSynSums(Connections cn, out int prevPresynSum, out int segSynSum) + { + prevPresynSum = 0; + segSynSum = 0; + + for (int i = 0; i < cn.HtmConfig.NumColumns; i++) + { + var col = cn.GetColumn(i); + foreach (var cell in col.Cells) + { + prevPresynSum += cell.ReceptorSynapses.Count; - Assert.IsTrue(segMapBefore != cn.GetSegmentMapping()); - Assert.IsTrue(segMapBefore.Keys.SequenceEqual(cn.GetSegmentMapping().Keys)); + foreach (var seg in cell.DistalDendrites) + { + segSynSum += seg.Synapses.Count; + } + } + } } public void TestLeastUsedCell() diff --git a/NeoCortexApi/UnitTestsProject/UnitTest1.cs b/NeoCortexApi/UnitTestsProject/UnitTest1.cs index 3b5e28a56..362661c64 100644 --- a/NeoCortexApi/UnitTestsProject/UnitTest1.cs +++ b/NeoCortexApi/UnitTestsProject/UnitTest1.cs @@ -166,7 +166,7 @@ public void CompareDentrites() [DataRow(30)] [DataRow(40)] [DataRow(50)] - [TestCategory("Prod")] + // [TestCategory("Prod")] public void TestHeatmapCreation(int threshold) { List bostArrays = new List(); diff --git a/NeoCortexApi/UnitTestsProject/UnitTestsProject.csproj b/NeoCortexApi/UnitTestsProject/UnitTestsProject.csproj index edac8748a..0581e506a 100644 --- a/NeoCortexApi/UnitTestsProject/UnitTestsProject.csproj +++ b/NeoCortexApi/UnitTestsProject/UnitTestsProject.csproj @@ -8,6 +8,13 @@ TRACE;USE_AKKA + full + true + + + + full + true @@ -24,7 +31,6 @@ - diff --git a/NeoCortexApi/WebSocketNeuroVisualizer/WebSocketNeuroVisualizer.csproj b/NeoCortexApi/WebSocketNeuroVisualizer/WebSocketNeuroVisualizer.csproj index fef1422de..b2bb61db5 100644 --- a/NeoCortexApi/WebSocketNeuroVisualizer/WebSocketNeuroVisualizer.csproj +++ b/NeoCortexApi/WebSocketNeuroVisualizer/WebSocketNeuroVisualizer.csproj @@ -1,9 +1,19 @@ - + netstandard2.1 + + full + true + + + + full + true + + diff --git a/README.md b/README.md index 7a032598b..dbd8bf92a 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ https://github.com/numenta HTM Community: https://numenta.org/ -A deepe dive in HTM Temporal Memory algorithm: +A deep dive in HTM Temporal Memory algorithm: https://numenta.com/assets/pdf/temporal-memory-algorithm/Temporal-Memory-Algorithm-Details.pdf Continious Online Sequence Learning with HTM: diff --git a/Results/SequenceLearning/Example of none completed experiment.txt b/Results/SequenceLearning/Example of none completed experiment.txt new file mode 100644 index 000000000..db1da8315 --- /dev/null +++ b/Results/SequenceLearning/Example of none completed experiment.txt @@ -0,0 +1,44 @@ +Some of cell SDRs generate more then 20 active cells. This is because such SDRs activate cells of more than one pattern. +Sometimes TM learns faster and long SDRs will be reduced to the expected lenght (In this case 2% of 1024 columns = 20) +--------------------------------------------------------------------------------------------------------------------------- + +Active segments: 36, Matching segments: 159 +Col SDR: 161, 175, 654, 666, 667, 668, 670, 674, 684, 688, 692, 693, 695, 697, 702, 708, 710, 717, 722, 736, +Cell SDR: 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4375, 4376, 4377, 4378, 4379, 4380, 4381, 4382, 4383, 4384, 4385, 4386, 4387, 4388, 4389, 4390, 4391, 4392, 4393, 4394, 4395, 4396, 4397, 4398, 4399, 16350, 16351, 16352, 16353, 16354, 16355, 16356, 16357, 16358, 16359, 16360, 16361, 16362, 16363, 16364, 16365, 16366, 16367, 16368, 16369, 16370, 16371, 16372, 16373, 16374, 16650, 16651, 16652, 16653, 16654, 16655, 16656, 16657, 16658, 16659, 16660, 16661, 16662, 16663, 16664, 16665, 16666, 16667, 16668, 16669, 16670, 16671, 16672, 16673, 16674, 16675, 16676, 16677, 16678, 16679, 16680, 16681, 16682, 16683, 16684, 16685, 16686, 16687, 16688, 16689, 16690, 16691, 16692, 16693, 16694, 16695, 16696, 16697, 16698, 16699, 16700, 16701, 16702, 16703, 16704, 16705, 16706, 16707, 16708, 16709, 16710, 16711, 16712, 16713, 16714, 16715, 16716, 16717, 16718, 16719, 16720, 16721, 16722, 16723, 16724, 16750, 16751, 16752, 16753, 16754, 16755, 16756, 16757, 16758, 16759, 16760, 16761, 16762, 16763, 16764, 16765, 16766, 16767, 16768, 16769, 16770, 16771, 16772, 16773, 16774, 16850, 16851, 16852, 16853, 16854, 16855, 16856, 16857, 16858, 16859, 16860, 16861, 16862, 16863, 16864, 16865, 16866, 16867, 16868, 16869, 16870, 16871, 16872, 16873, 16874, 17100, 17101, 17102, 17103, 17104, 17105, 17106, 17107, 17108, 17109, 17110, 17111, 17112, 17113, 17114, 17115, 17116, 17117, 17118, 17119, 17120, 17121, 17122, 17123, 17124, 17200, 17201, 17202, 17203, 17204, 17205, 17206, 17207, 17208, 17209, 17210, 17211, 17212, 17213, 17214, 17215, 17216, 17217, 17218, 17219, 17220, 17221, 17222, 17223, 17224, 17300, 17301, 17302, 17303, 17304, 17305, 17306, 17307, 17308, 17309, 17310, 17311, 17312, 17313, 17314, 17315, 17316, 17317, 17318, 17319, 17320, 17321, 17322, 17323, 17324, 17325, 17326, 17327, 17328, 17329, 17330, 17331, 17332, 17333, 17334, 17335, 17336, 17337, 17338, 17339, 17340, 17341, 17342, 17343, 17344, 17345, 17346, 17347, 17348, 17349, 17375, 17376, 17377, 17378, 17379, 17380, 17381, 17382, 17383, 17384, 17385, 17386, 17387, 17388, 17389, 17390, 17391, 17392, 17393, 17394, 17395, 17396, 17397, 17398, 17399, 17425, 17426, 17427, 17428, 17429, 17430, 17431, 17432, 17433, 17434, 17435, 17436, 17437, 17438, 17439, 17440, 17441, 17442, 17443, 17444, 17445, 17446, 17447, 17448, 17449, 17550, 17551, 17552, 17553, 17554, 17555, 17556, 17557, 17558, 17559, 17560, 17561, 17562, 17563, 17564, 17565, 17566, 17567, 17568, 17569, 17570, 17571, 17572, 17573, 17574, 17700, 17701, 17702, 17703, 17704, 17705, 17706, 17707, 17708, 17709, 17710, 17711, 17712, 17713, 17714, 17715, 17716, 17717, 17718, 17719, 17720, 17721, 17722, 17723, 17724, 17750, 17751, 17752, 17753, 17754, 17755, 17756, 17757, 17758, 17759, 17760, 17761, 17762, 17763, 17764, 17765, 17766, 17767, 17768, 17769, 17770, 17771, 17772, 17773, 17774, 17925, 17926, 17927, 17928, 17929, 17930, 17931, 17932, 17933, 17934, 17935, 17936, 17937, 17938, 17939, 17940, 17941, 17942, 17943, 17944, 17945, 17946, 17947, 17948, 17949, 18050, 18051, 18052, 18053, 18054, 18055, 18056, 18057, 18058, 18059, 18060, 18061, 18062, 18063, 18064, 18065, 18066, 18067, 18068, 18069, 18070, 18071, 18072, 18073, 18074, 18400, 18401, 18402, 18403, 18404, 18405, 18406, 18407, 18408, 18409, 18410, 18411, 18412, 18413, 18414, 18415, 18416, 18417, 18418, 18419, 18420, 18421, 18422, 18423, 18424, +Missmatch! Actual value: 6-5-4-3-7-1-9-12-11-12-13-14-11-12-14-5-7-6-9-3-4-3-4-3-4-0-1-0-2-3-4-5 - Predicted value: +Item length: 35 Items: 32 +Predictive cells: 35 5826, 6040, 6246, 6629, 14742, 15904, 16132, 16482, 17562, 17936, 18185, 18201, 18410, 18415, 18421, 18513, 18703, 18706, 18711, 18868, 18955, 18960, 18967, 19180, 19188, 19340, 19389, 19504, 19510, 19513, 19658, 19677, 19679, 19689, 19703, +indx:0 inp/len: 6-5-4-3-7-1-9-12-11-12-13-14-11-12-14-5-7-6-9-3-4-3-4-3-4-0-1-0-2-3-4-5/500 = similarity 5 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4375, 4376, 4377, 4378, 4379, 4380, 4381, 4382, 4383, 4384, 4385, 4386, 4387, 4388, 4389, 4390, 4391, 4392, 4393, 4394, 4395, 4396, 4397, 4398, 4399, 16350, 16351, 16352, 16353, 16354, 16355, 16356, 16357, 16358, 16359, 16360, 16361, 16362, 16363, 16364, 16365, 16366, 16367, 16368, 16369, 16370, 16371, 16372, 16373, 16374, 16650, 16651, 16652, 16653, 16654, 16655, 16656, 16657, 16658, 16659, 16660, 16661, 16662, 16663, 16664, 16665, 16666, 16667, 16668, 16669, 16670, 16671, 16672, 16673, 16674, 16675, 16676, 16677, 16678, 16679, 16680, 16681, 16682, 16683, 16684, 16685, 16686, 16687, 16688, 16689, 16690, 16691, 16692, 16693, 16694, 16695, 16696, 16697, 16698, 16699, 16700, 16701, 16702, 16703, 16704, 16705, 16706, 16707, 16708, 16709, 16710, 16711, 16712, 16713, 16714, 16715, 16716, 16717, 16718, 16719, 16720, 16721, 16722, 16723, 16724, 16750, 16751, 16752, 16753, 16754, 16755, 16756, 16757, 16758, 16759, 16760, 16761, 16762, 16763, 16764, 16765, 16766, 16767, 16768, 16769, 16770, 16771, 16772, 16773, 16774, 16850, 16851, 16852, 16853, 16854, 16855, 16856, 16857, 16858, 16859, 16860, 16861, 16862, 16863, 16864, 16865, 16866, 16867, 16868, 16869, 16870, 16871, 16872, 16873, 16874, 17100, 17101, 17102, 17103, 17104, 17105, 17106, 17107, 17108, 17109, 17110, 17111, 17112, 17113, 17114, 17115, 17116, 17117, 17118, 17119, 17120, 17121, 17122, 17123, 17124, 17200, 17201, 17202, 17203, 17204, 17205, 17206, 17207, 17208, 17209, 17210, 17211, 17212, 17213, 17214, 17215, 17216, 17217, 17218, 17219, 17220, 17221, 17222, 17223, 17224, 17300, 17301, 17302, 17303, 17304, 17305, 17306, 17307, 17308, 17309, 17310, 17311, 17312, 17313, 17314, 17315, 17316, 17317, 17318, 17319, 17320, 17321, 17322, 17323, 17324, 17325, 17326, 17327, 17328, 17329, 17330, 17331, 17332, 17333, 17334, 17335, 17336, 17337, 17338, 17339, 17340, 17341, 17342, 17343, 17344, 17345, 17346, 17347, 17348, 17349, 17375, 17376, 17377, 17378, 17379, 17380, 17381, 17382, 17383, 17384, 17385, 17386, 17387, 17388, 17389, 17390, 17391, 17392, 17393, 17394, 17395, 17396, 17397, 17398, 17399, 17425, 17426, 17427, 17428, 17429, 17430, 17431, 17432, 17433, 17434, 17435, 17436, 17437, 17438, 17439, 17440, 17441, 17442, 17443, 17444, 17445, 17446, 17447, 17448, 17449, 17550, 17551, 17552, 17553, 17554, 17555, 17556, 17557, 17558, 17559, 17560, 17561, 17562, 17563, 17564, 17565, 17566, 17567, 17568, 17569, 17570, 17571, 17572, 17573, 17574, 17700, 17701, 17702, 17703, 17704, 17705, 17706, 17707, 17708, 17709, 17710, 17711, 17712, 17713, 17714, 17715, 17716, 17717, 17718, 17719, 17720, 17721, 17722, 17723, 17724, 17750, 17751, 17752, 17753, 17754, 17755, 17756, 17757, 17758, 17759, 17760, 17761, 17762, 17763, 17764, 17765, 17766, 17767, 17768, 17769, 17770, 17771, 17772, 17773, 17774, 17925, 17926, 17927, 17928, 17929, 17930, 17931, 17932, 17933, 17934, 17935, 17936, 17937, 17938, 17939, 17940, 17941, 17942, 17943, 17944, 17945, 17946, 17947, 17948, 17949, 18050, 18051, 18052, 18053, 18054, 18055, 18056, 18057, 18058, 18059, 18060, 18061, 18062, 18063, 18064, 18065, 18066, 18067, 18068, 18069, 18070, 18071, 18072, 18073, 18074, 18400, 18401, 18402, 18403, 18404, 18405, 18406, 18407, 18408, 18409, 18410, 18411, 18412, 18413, 18414, 18415, 18416, 18417, 18418, 18419, 18420, 18421, 18422, 18423, 18424, +>indx:0 inp/len: 5-4-3-7-1-9-12-11-12-13-14-11-12-14-5-7-6-9-3-4-3-4-3-4-0-1-0-2-3-4-5-6/31 = similarity 31 5826, 6040, 6246, 6629, 17562, 17936, 18185, 18201, 18410, 18415, 18421, 18513, 18703, 18706, 18711, 18868, 18955, 18960, 18967, 19180, 19188, 19340, 19389, 19504, 19510, 19513, 19658, 19677, 19679, 19689, 19703, +