From 95b8dfee87a8caab33955e1f691ca4d6a89cd24a Mon Sep 17 00:00:00 2001 From: root Date: Thu, 20 Feb 2020 11:55:17 -0800 Subject: [PATCH 01/43] Naive implementations of optimizations suggested by breznak --- src/htm/algorithms/SpatialPooler.cpp | 64 ++++++++++++++++++++++------ src/htm/algorithms/SpatialPooler.hpp | 5 +++ 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 594d84b6ad..67839a4355 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -442,6 +442,8 @@ void SpatialPooler::initialize( } updateInhibitionRadius_(); + calculateWrapAroundNeighbors(); + mapAllNeighbors(); if (spVerbosity_ > 0) { printParameters(); @@ -460,6 +462,8 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea boostOverlaps_(overlaps, boostedOverlaps_); auto &activeVector = active.getSparse(); +// if (!wrapAroundNeighborsisset) +// calculateWrapAroundNeighbors(); inhibitColumns_(boostedOverlaps_, activeVector); // Notify the active SDR that its internal data vector has changed. Always // call SDR's setter methods even if when modifying the SDR's own data @@ -473,7 +477,12 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea bumpUpWeakColumns_(); updateBoostFactors_(); if (isUpdateRound_()) { + const UInt old_inhibitionRadius_ = inhibitionRadius_; updateInhibitionRadius_(); + if (inhibitionRadius_ != old_inhibitionRadius_) { + calculateWrapAroundNeighbors(); + mapAllNeighbors(); + } updateMinDutyCycles_(); } } @@ -481,6 +490,27 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea return overlaps; } +void SpatialPooler::calculateWrapAroundNeighbors() { + wrapAroundNeighbors_ = 1; + for (UInt i = 0; i &overlaps, //TODO use Eigen sparse vector here vector &boosted) const { @@ -768,9 +798,15 @@ void SpatialPooler::updateBoostFactorsLocal_() { Real localActivityDensity = 0.0f; if (wrapAround_) { - for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) { + numNeighbors = 0; // In wrapAround, number of neighbors to be cons idered is solely a function of the inhibition radius, the number of dimensi ons, and of the size of each of those dimenion + numNeighbors = wrapAroundNeighbors_; + const UInt column = i; + const UInt mapOffset = column * (numNeighbors+1); +// for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) { + for (UInt j=0;j<(numNeighbors+1);j++) { + const UInt neighbor = neighborMap_[mapOffset + j]; +// std::cout << "N: " << neighbor << " -- "; localActivityDensity += activeDutyCycles_[neighbor]; - numNeighbors += 1; } } else { for(auto neighbor: Neighborhood(i, inhibitionRadius_, columnDimensions_)) { @@ -866,22 +902,22 @@ void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, if (wrapAround_) { - numNeighbors = 0; // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, - // ... the number of dimensions, and of the size of each of those dimenion - UInt predN = 1; - const UInt diam = 2*inhibitionRadius_ + 1; //the inh radius can change, that's why we recompute here - for (const auto dim : columnDimensions_) { - predN *= std::min(diam, dim); - } - predN -= 1; - numNeighbors = predN; - const UInt numActive_wrap = static_cast(0.5f + (density * (numNeighbors + 1))); - - for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) { //TODO if we don't change inh radius (changes only every isUpdateRound()), + numNeighbors = 0; // In wrapAround, number of neighbors to be cons idered is solely a function of the inhibition radius, the number of dimensi ons, and of the size of each of those dimenion + numNeighbors = wrapAroundNeighbors_; + + const UInt numActive_wrap = (UInt)(0.5f + (density * (numNeighbors + 1))); + + const UInt mapOffset = column * (numNeighbors+1); + +// for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) { //TODO if we don't change inh radius (changes only every isUpdateRound()), // then these values can be cached -> faster local inh + for (UInt j=0;j<(numNeighbors+1);j++) { + const UInt neighbor = neighborMap_[mapOffset + j]; +// std::cout << "N: " << neighbor << " -- "; if (neighbor == column) { continue; } +// numNeighbors++; const Real difference = overlaps[neighbor] - overlaps[column]; if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { diff --git a/src/htm/algorithms/SpatialPooler.hpp b/src/htm/algorithms/SpatialPooler.hpp index fffd29ff87..483d095193 100644 --- a/src/htm/algorithms/SpatialPooler.hpp +++ b/src/htm/algorithms/SpatialPooler.hpp @@ -1108,6 +1108,9 @@ class SpatialPooler : public Serializable */ bool isUpdateRound_() const; + void calculateWrapAroundNeighbors(); + void mapAllNeighbors(); + //------------------------------------------------------------------- // Debugging helpers //------------------------------------------------------------------- @@ -1141,6 +1144,8 @@ class SpatialPooler : public Serializable Real localAreaDensity_; UInt stimulusThreshold_; UInt inhibitionRadius_; + UInt wrapAroundNeighbors_; + vector neighborMap_; UInt dutyCyclePeriod_; Real boostStrength_; UInt iterationNum_; From 8d580a7ba42c7fe6dd07e0bcd7548af9f0d017f0 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 20 Feb 2020 14:34:59 -0800 Subject: [PATCH 02/43] Fix a couple of mistakes. --- src/htm/algorithms/SpatialPooler.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 67839a4355..0ee576c334 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -151,7 +151,11 @@ void SpatialPooler::setStimulusThreshold(UInt stimulusThreshold) { UInt SpatialPooler::getInhibitionRadius() const { return inhibitionRadius_; } void SpatialPooler::setInhibitionRadius(UInt inhibitionRadius) { + if (inhibitionRadius_ != inhibitionRadius) { inhibitionRadius_ = inhibitionRadius; + calculateWrapAroundNeighbors(); + mapAllNeighbors(); + } } UInt SpatialPooler::getDutyCyclePeriod() const { return dutyCyclePeriod_; } @@ -807,7 +811,10 @@ void SpatialPooler::updateBoostFactorsLocal_() { const UInt neighbor = neighborMap_[mapOffset + j]; // std::cout << "N: " << neighbor << " -- "; localActivityDensity += activeDutyCycles_[neighbor]; +// numNeighbors += 1; +// std::cout << "numneigh: " < Date: Sat, 22 Feb 2020 12:17:23 -0800 Subject: [PATCH 03/43] Even more performance improvements. --- src/htm/algorithms/SpatialPooler.cpp | 46 +++++++++++++++++++++------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 0ee576c334..b06bc37a97 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -797,16 +797,16 @@ void SpatialPooler::updateBoostFactorsGlobal_() { void SpatialPooler::updateBoostFactorsLocal_() { +if (wrapAround_) { for (UInt i = 0; i < numColumns_; ++i) { UInt numNeighbors = 0u; Real localActivityDensity = 0.0f; - if (wrapAround_) { numNeighbors = 0; // In wrapAround, number of neighbors to be cons idered is solely a function of the inhibition radius, the number of dimensi ons, and of the size of each of those dimenion numNeighbors = wrapAroundNeighbors_; const UInt column = i; const UInt mapOffset = column * (numNeighbors+1); -// for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) { +// for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) P for (UInt j=0;j<(numNeighbors+1);j++) { const UInt neighbor = neighborMap_[mapOffset + j]; // std::cout << "N: " << neighbor << " -- "; @@ -815,17 +815,24 @@ void SpatialPooler::updateBoostFactorsLocal_() { // std::cout << "numneigh: " < &overlaps, // Tie-breaking: when overlaps are equal, columns that have already been // selected are treated as "bigger". vector activeColumnsDense(numColumns_, false); + + + if (wrapAround_) { for (UInt column = 0; column < numColumns_; column++) { if (overlaps[column] < stimulusThreshold_) { @@ -908,15 +918,14 @@ void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, UInt numBigger = 0; - if (wrapAround_) { numNeighbors = 0; // In wrapAround, number of neighbors to be cons idered is solely a function of the inhibition radius, the number of dimensi ons, and of the size of each of those dimenion numNeighbors = wrapAroundNeighbors_; - const UInt numActive_wrap = (UInt)(0.5f + (density * (numNeighbors + 1))); + const UInt numActive_wrap = static_cast(0.5f + (density * (numNeighbors + 1))); const UInt mapOffset = column * (numNeighbors+1); -// for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) { //TODO if we don't change inh radius (changes only every isUpdateRound()), +// for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) P //TODO if we don't change inh radius (changes only every isUpdateRound()), // then these values can be cached -> faster local inh for (UInt j=0;j<(numNeighbors+1);j++) { const UInt neighbor = neighborMap_[mapOffset + j]; @@ -932,7 +941,23 @@ void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, if (numBigger >= numActive_wrap) { break; } } } - } else { + const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); + if (numBigger < numActive) { + activeColumns.push_back(column); + activeColumnsDense[column] = true; + } + } + + } else { + for (UInt column = 0; column < numColumns_; column++) { + if (overlaps[column] < stimulusThreshold_) { + continue; + } + + UInt numNeighbors = 0; + UInt numBigger = 0; + + for(auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_)) { if (neighbor == column) { continue; @@ -943,15 +968,14 @@ void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { numBigger++; } - } } - - const UInt numActive = (UInt)(0.5f + (density * (numNeighbors + 1))); + const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); if (numBigger < numActive) { activeColumns.push_back(column); activeColumnsDense[column] = true; } } + } } From f5ba4e51f0a1ee20e333dda1f627275b480625af Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Tue, 17 Mar 2020 20:19:49 +0100 Subject: [PATCH 04/43] SP: cleanup, UInt calculateWrapAroundNeighbors_() is now UInt bla_() const small fixes in code cleanup --- src/htm/algorithms/SpatialPooler.cpp | 59 ++++++++++++++-------------- src/htm/algorithms/SpatialPooler.hpp | 2 +- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index b06bc37a97..4bc09dda25 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -151,10 +151,13 @@ void SpatialPooler::setStimulusThreshold(UInt stimulusThreshold) { UInt SpatialPooler::getInhibitionRadius() const { return inhibitionRadius_; } void SpatialPooler::setInhibitionRadius(UInt inhibitionRadius) { + NTA_ASSERT(inhibitionRadius > 0); if (inhibitionRadius_ != inhibitionRadius) { - inhibitionRadius_ = inhibitionRadius; - calculateWrapAroundNeighbors(); - mapAllNeighbors(); + inhibitionRadius_ = inhibitionRadius; + if (wrapAround_) { + wrapAroundNeighbors_ = calculateWrapAroundNeighbors_(); + mapAllNeighbors(); + } } } @@ -446,8 +449,10 @@ void SpatialPooler::initialize( } updateInhibitionRadius_(); - calculateWrapAroundNeighbors(); - mapAllNeighbors(); + if(wrapAround) { + wrapAroundNeighbors_ = calculateWrapAroundNeighbors_(); + mapAllNeighbors(); + } if (spVerbosity_ > 0) { printParameters(); @@ -466,8 +471,6 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea boostOverlaps_(overlaps, boostedOverlaps_); auto &activeVector = active.getSparse(); -// if (!wrapAroundNeighborsisset) -// calculateWrapAroundNeighbors(); inhibitColumns_(boostedOverlaps_, activeVector); // Notify the active SDR that its internal data vector has changed. Always // call SDR's setter methods even if when modifying the SDR's own data @@ -481,12 +484,7 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea bumpUpWeakColumns_(); updateBoostFactors_(); if (isUpdateRound_()) { - const UInt old_inhibitionRadius_ = inhibitionRadius_; updateInhibitionRadius_(); - if (inhibitionRadius_ != old_inhibitionRadius_) { - calculateWrapAroundNeighbors(); - mapAllNeighbors(); - } updateMinDutyCycles_(); } } @@ -494,14 +492,19 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea return overlaps; } -void SpatialPooler::calculateWrapAroundNeighbors() { - wrapAroundNeighbors_ = 1; - for (UInt i = 0; i(round(radius))); } @@ -797,28 +801,25 @@ void SpatialPooler::updateBoostFactorsGlobal_() { void SpatialPooler::updateBoostFactorsLocal_() { -if (wrapAround_) { + if (wrapAround_) { for (UInt i = 0; i < numColumns_; ++i) { UInt numNeighbors = 0u; Real localActivityDensity = 0.0f; - numNeighbors = 0; // In wrapAround, number of neighbors to be cons idered is solely a function of the inhibition radius, the number of dimensi ons, and of the size of each of those dimenion + numNeighbors = 0; // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, the number of dimensions, and of the size of each of those dimenions numNeighbors = wrapAroundNeighbors_; const UInt column = i; const UInt mapOffset = column * (numNeighbors+1); // for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) P for (UInt j=0;j<(numNeighbors+1);j++) { - const UInt neighbor = neighborMap_[mapOffset + j]; -// std::cout << "N: " << neighbor << " -- "; - localActivityDensity += activeDutyCycles_[neighbor]; -// numNeighbors += 1; -// std::cout << "numneigh: " < Date: Tue, 17 Mar 2020 22:38:28 +0100 Subject: [PATCH 05/43] SP cached wrapping neighborhood: use map for cache store cached neighbors in map, rather than vector. makes code easier to read. --- src/htm/algorithms/SpatialPooler.cpp | 54 ++++++++++++---------------- src/htm/algorithms/SpatialPooler.hpp | 4 ++- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 4bc09dda25..1edae0c34a 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -509,12 +509,13 @@ UInt SpatialPooler::calculateWrapAroundNeighbors_() const { void SpatialPooler::mapAllNeighbors() { neighborMap_.clear(); - neighborMap_.reserve(wrapAroundNeighbors_ * numColumns_); - for(UInt column=0; column neighbors; //of the current column + for(const auto neighbor: WrappingNeighborhood(column, inhibitionRadius_, columnDimensions_)) { + neighbors.push_back(neighbor); } - if(neighborMap_.size() != (column+1)*(wrapAroundNeighbors_+1)) { std::cout << "error NE - siz " << neighborMap_.size() << ", id " << (column+1)*wrapAroundNeighbors_ << " \n"; } + neighborMap_[column] = neighbors; } } @@ -794,7 +795,7 @@ void applyBoosting_(const UInt i, void SpatialPooler::updateBoostFactorsGlobal_() { const Real targetDensity = localAreaDensity_; - for (UInt i = 0; i < numColumns_; ++i) { + for (UInt i = 0; i < numColumns_; ++i) { applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_); } } @@ -802,20 +803,18 @@ void SpatialPooler::updateBoostFactorsGlobal_() { void SpatialPooler::updateBoostFactorsLocal_() { if (wrapAround_) { + // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, + // the number of dimensions, and of the size of each of those dimenions + const UInt numNeighbors = wrapAroundNeighbors_ + 1; // +1 bcs this function counts the column itself as a neighbor + for (UInt i = 0; i < numColumns_; ++i) { - UInt numNeighbors = 0u; Real localActivityDensity = 0.0f; - numNeighbors = 0; // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, the number of dimensions, and of the size of each of those dimenions - numNeighbors = wrapAroundNeighbors_; - const UInt column = i; - const UInt mapOffset = column * (numNeighbors+1); + const auto& hood = neighborMap_.at(i); //hood is vector<> // for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) P - for (UInt j=0;j<(numNeighbors+1);j++) { - const UInt neighbor = neighborMap_[mapOffset + j]; //TODO make this a key-value map to avoid all the coding around + for (const auto neighbor : hood) { localActivityDensity += activeDutyCycles_[neighbor]; } - numNeighbors++; // this function counts the column itself as a neighbor const Real targetDensity = localActivityDensity / numNeighbors; applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_); } @@ -915,34 +914,25 @@ void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, continue; } - UInt numNeighbors = 0; UInt numBigger = 0; - - - numNeighbors = 0; // In wrapAround, number of neighbors to be cons idered is solely a function of the inhibition radius, the number of dimensi ons, and of the size of each of those dimenion - numNeighbors = wrapAroundNeighbors_; - - const UInt numActive_wrap = static_cast(0.5f + (density * (numNeighbors + 1))); - - const UInt mapOffset = column * (numNeighbors+1); - -// for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) P //TODO if we don't change inh radius (changes only every isUpdateRound()), - // then these values can be cached -> faster local inh - for (UInt j=0;j<(numNeighbors+1);j++) { - const UInt neighbor = neighborMap_[mapOffset + j]; -// std::cout << "N: " << neighbor << " -- "; + // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, + // the number of dimensions, and of the size of each of those dimenion + const UInt numNeighbors = wrapAroundNeighbors_; + const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); + + const auto& hood = neighborMap_.at(column); + //for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) P + for (const auto neighbor: hood) { if (neighbor == column) { continue; } -// numNeighbors++; const Real difference = overlaps[neighbor] - overlaps[column]; if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { numBigger++; - if (numBigger >= numActive_wrap) { break; } + if (numBigger >= numActive) { break; } } } - const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); if (numBigger < numActive) { activeColumns.push_back(column); activeColumnsDense[column] = true; diff --git a/src/htm/algorithms/SpatialPooler.hpp b/src/htm/algorithms/SpatialPooler.hpp index 6fc133515f..7a87f2c83e 100644 --- a/src/htm/algorithms/SpatialPooler.hpp +++ b/src/htm/algorithms/SpatialPooler.hpp @@ -24,6 +24,7 @@ #include #include +#include #include // std::setprecision #include #include @@ -1145,7 +1146,6 @@ class SpatialPooler : public Serializable UInt stimulusThreshold_; UInt inhibitionRadius_; UInt wrapAroundNeighbors_; - vector neighborMap_; UInt dutyCyclePeriod_; Real boostStrength_; UInt iterationNum_; @@ -1183,6 +1183,8 @@ class SpatialPooler : public Serializable public: const Connections& connections = connections_; //for inspection of details in connections. Const, so users cannot break the SP internals. const Connections& getConnections() const { return connections_; } // as above, but for use in pybind11 +private: + std::unordered_map> neighborMap_; // col -> vector neighbors }; std::ostream & operator<<(std::ostream & out, const SpatialPooler &sp); From 65fb9841a8ca0a86f75270b2b04d326471fe72d0 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 10:15:54 +0100 Subject: [PATCH 06/43] SP cached hood: drop wrapAroundNeighbors_ as not needed, we compute this from hood.size() --- src/htm/algorithms/SpatialPooler.cpp | 33 ++++++++-------------------- src/htm/algorithms/SpatialPooler.hpp | 2 -- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 1edae0c34a..1431252c9e 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -155,7 +155,6 @@ void SpatialPooler::setInhibitionRadius(UInt inhibitionRadius) { if (inhibitionRadius_ != inhibitionRadius) { inhibitionRadius_ = inhibitionRadius; if (wrapAround_) { - wrapAroundNeighbors_ = calculateWrapAroundNeighbors_(); mapAllNeighbors(); } } @@ -450,7 +449,6 @@ void SpatialPooler::initialize( updateInhibitionRadius_(); if(wrapAround) { - wrapAroundNeighbors_ = calculateWrapAroundNeighbors_(); mapAllNeighbors(); } @@ -492,20 +490,6 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea return overlaps; } -/** - * calculate number for neighbors for wrapping neighborhoods (wrapAround == true) - * this is a performance optimization as this number does not change in runtime - **/ -UInt SpatialPooler::calculateWrapAroundNeighbors_() const { - NTA_ASSERT(wrapAround_ == true); - UInt wrapAroundNeighbors = 1; - for (UInt i = 0; i -// for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) P + const auto& hood = neighborMap_.at(i); //hood is vector<> + // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, + // the number of dimensions, and of the size of each of those dimenions + const UInt numNeighbors = hood.size(); + + //for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) P for (const auto neighbor : hood) { localActivityDensity += activeDutyCycles_[neighbor]; } @@ -915,12 +900,12 @@ void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, } UInt numBigger = 0; + const auto& hood = neighborMap_.at(column); // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenion - const UInt numNeighbors = wrapAroundNeighbors_; + const UInt numNeighbors = hood.size()-1; // -1 bcs "hood" includes the column itself (center) const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); - const auto& hood = neighborMap_.at(column); //for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) P for (const auto neighbor: hood) { if (neighbor == column) { diff --git a/src/htm/algorithms/SpatialPooler.hpp b/src/htm/algorithms/SpatialPooler.hpp index 7a87f2c83e..a5a295a0b8 100644 --- a/src/htm/algorithms/SpatialPooler.hpp +++ b/src/htm/algorithms/SpatialPooler.hpp @@ -1109,7 +1109,6 @@ class SpatialPooler : public Serializable */ bool isUpdateRound_() const; - UInt calculateWrapAroundNeighbors_() const; void mapAllNeighbors(); //------------------------------------------------------------------- @@ -1145,7 +1144,6 @@ class SpatialPooler : public Serializable Real localAreaDensity_; UInt stimulusThreshold_; UInt inhibitionRadius_; - UInt wrapAroundNeighbors_; UInt dutyCyclePeriod_; Real boostStrength_; UInt iterationNum_; From f59435610290276d4cbf47b5d6319b53b9c9a7b5 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 11:08:57 +0100 Subject: [PATCH 07/43] SP cached hood: for both wrap, non-wrap hoods now mapAllNeighbors() supports also the non-wrapping variant. ALso made const --- src/examples/hotgym/HelloSPTP.cpp | 1 + src/htm/algorithms/SpatialPooler.cpp | 63 ++++++++++++++++------------ src/htm/algorithms/SpatialPooler.hpp | 2 +- 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/examples/hotgym/HelloSPTP.cpp b/src/examples/hotgym/HelloSPTP.cpp index 02aae9c763..bb89bd9dfe 100644 --- a/src/examples/hotgym/HelloSPTP.cpp +++ b/src/examples/hotgym/HelloSPTP.cpp @@ -72,6 +72,7 @@ EPOCHS = 2; // make test faster in Debug SpatialPooler spLocal(enc.dimensions, vector{COLS}); // Spatial pooler with local inh spGlobal.setGlobalInhibition(true); spLocal.setGlobalInhibition(false); + spLocal.setWrapAround(false); Random rnd(42); //uses fixed seed for deterministic output checks TemporalMemory tm(vector{COLS}, CELLS); diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 1431252c9e..9bbc102bca 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -154,9 +154,7 @@ void SpatialPooler::setInhibitionRadius(UInt inhibitionRadius) { NTA_ASSERT(inhibitionRadius > 0); if (inhibitionRadius_ != inhibitionRadius) { inhibitionRadius_ = inhibitionRadius; - if (wrapAround_) { - mapAllNeighbors(); - } + neighborMap_ = mapAllNeighbors(); } } @@ -448,9 +446,7 @@ void SpatialPooler::initialize( } updateInhibitionRadius_(); - if(wrapAround) { - mapAllNeighbors(); - } + neighborMap_ = mapAllNeighbors(); if (spVerbosity_ > 0) { printParameters(); @@ -491,16 +487,29 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea } -void SpatialPooler::mapAllNeighbors() { - neighborMap_.clear(); - neighborMap_.reserve(numColumns_); - for(UInt column=0; column < numColumns_; column++) { - vector neighbors; //of the current column - for(const auto neighbor: WrappingNeighborhood(column, inhibitionRadius_, columnDimensions_)) { - neighbors.push_back(neighbor); - } - neighborMap_[column] = neighbors; - } +unordered_map> SpatialPooler::mapAllNeighbors() const { + std::unordered_map> neighborMap; + neighborMap.reserve(numColumns_); + + if (wrapAround_) { + + for(UInt column=0; column < numColumns_; column++) { + vector neighbors; //of the current column + for(const auto neighbor: WrappingNeighborhood(column, inhibitionRadius_, columnDimensions_)) { + neighbors.push_back(neighbor); + } + neighborMap[column] = neighbors; + } + } else { + for(UInt column=0; column < numColumns_; column++) { + vector neighbors; //of the current column + for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_)) { + neighbors.push_back(neighbor); + } + neighborMap[column] = neighbors; + } + } + return neighborMap; } @@ -791,15 +800,15 @@ void SpatialPooler::updateBoostFactorsLocal_() { for (UInt i = 0; i < numColumns_; ++i) { Real localActivityDensity = 0.0f; - const auto& hood = neighborMap_.at(i); //hood is vector<> + const auto& hood = neighborMap_.at(i); //hood is vector<> of cached neighborhood values + + //for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) P + for (const auto neighbor : hood) { + localActivityDensity += activeDutyCycles_[neighbor]; + } // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenions const UInt numNeighbors = hood.size(); - - //for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) P - for (const auto neighbor : hood) { - localActivityDensity += activeDutyCycles_[neighbor]; - } const Real targetDensity = localActivityDensity / numNeighbors; applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_); } @@ -807,12 +816,12 @@ void SpatialPooler::updateBoostFactorsLocal_() { for (UInt i = 0; i < numColumns_; ++i) { UInt numNeighbors = 0u; Real localActivityDensity = 0.0f; + const auto& hood = neighborMap_.at(i); //hood is vector<> of cached neighborhood values - - for(auto neighbor: Neighborhood(i, inhibitionRadius_, columnDimensions_)) { - localActivityDensity += activeDutyCycles_[neighbor]; - numNeighbors += 1; - } + for(const auto neighbor: hood) { + localActivityDensity += activeDutyCycles_[neighbor]; + numNeighbors += 1; + } const Real targetDensity = localActivityDensity / numNeighbors; applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_); } diff --git a/src/htm/algorithms/SpatialPooler.hpp b/src/htm/algorithms/SpatialPooler.hpp index a5a295a0b8..30e88e823e 100644 --- a/src/htm/algorithms/SpatialPooler.hpp +++ b/src/htm/algorithms/SpatialPooler.hpp @@ -1109,7 +1109,7 @@ class SpatialPooler : public Serializable */ bool isUpdateRound_() const; - void mapAllNeighbors(); + std::unordered_map> mapAllNeighbors() const; //------------------------------------------------------------------- // Debugging helpers From b5bf1e68cebe890c835aefa9c3d48838892449e6 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 13:50:57 +0100 Subject: [PATCH 08/43] SP inhibitColumns() returns active array return active columns as result --- src/examples/hotgym/HelloSPTP.cpp | 2 +- src/htm/algorithms/SpatialPooler.cpp | 52 +++++++++++-------- src/htm/algorithms/SpatialPooler.hpp | 22 ++++---- .../unit/algorithms/SpatialPoolerTest.cpp | 36 ++++++------- 4 files changed, 58 insertions(+), 54 deletions(-) diff --git a/src/examples/hotgym/HelloSPTP.cpp b/src/examples/hotgym/HelloSPTP.cpp index bb89bd9dfe..e052221c6a 100644 --- a/src/examples/hotgym/HelloSPTP.cpp +++ b/src/examples/hotgym/HelloSPTP.cpp @@ -72,7 +72,7 @@ EPOCHS = 2; // make test faster in Debug SpatialPooler spLocal(enc.dimensions, vector{COLS}); // Spatial pooler with local inh spGlobal.setGlobalInhibition(true); spLocal.setGlobalInhibition(false); - spLocal.setWrapAround(false); + //spLocal.setWrapAround(false); Random rnd(42); //uses fixed seed for deterministic output checks TemporalMemory tm(vector{COLS}, CELLS); diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 9bbc102bca..e0f4dfc08b 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -464,8 +464,7 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea boostOverlaps_(overlaps, boostedOverlaps_); - auto &activeVector = active.getSparse(); - inhibitColumns_(boostedOverlaps_, activeVector); + auto activeVector = inhibitColumns_(boostedOverlaps_); // Notify the active SDR that its internal data vector has changed. Always // call SDR's setter methods even if when modifying the SDR's own data // inplace. @@ -837,35 +836,31 @@ void SpatialPooler::updateBookeepingVars_(bool learn) { } -void SpatialPooler::inhibitColumns_(const vector &overlaps, - vector &activeColumns) const { +vector SpatialPooler::inhibitColumns_(const vector &overlaps) const { const Real density = localAreaDensity_; + NTA_ASSERT(!overlaps.empty()); + NTA_ASSERT(density > 0.0f && density <= 1.0f); if (globalInhibition_ || inhibitionRadius_ > *max_element(columnDimensions_.begin(), columnDimensions_.end())) { - inhibitColumnsGlobal_(overlaps, density, activeColumns); + return inhibitColumnsGlobal_(overlaps, density); } else { - inhibitColumnsLocal_(overlaps, density, activeColumns); + return inhibitColumnsLocal_(overlaps, density); } } -void SpatialPooler::inhibitColumnsGlobal_(const vector &overlaps, - Real density, - vector &activeColumns) const { - NTA_ASSERT(!overlaps.empty()); - NTA_ASSERT(density > 0.0f && density <= 1.0f); - - activeColumns.clear(); - const UInt numDesired = (UInt)(density * numColumns_); +vector SpatialPooler::inhibitColumnsGlobal_(const vector &overlaps, + const Real density) const { + const UInt numDesired = static_cast((density * numColumns_)); NTA_CHECK(numDesired > 0) << "Not enough columns (" << numColumns_ << ") " << "for desired density (" << density << ")."; // Sort the columns by the amount of overlap. First make a list of all of the // column indexes. - activeColumns.reserve(numColumns_); - for(UInt i = 0; i < numColumns_; i++) - activeColumns.push_back(i); + vector activeColumns(numColumns_); + std::iota(activeColumns.begin(), activeColumns.end(), 0); //fill with sequence 0,1,..N + // Compare the column indexes by their overlap. auto compare = [&overlaps](const UInt &a, const UInt &b) -> bool {return (overlaps[a] == overlaps[b]) ? a > b : overlaps[a] > overlaps[b];}; //for determinism if overlaps match (tieBreaker does not solve that), @@ -886,15 +881,20 @@ void SpatialPooler::inhibitColumnsGlobal_(const vector &overlaps, std::sort(activeColumns.begin(), activeColumns.end(), compare); // Remove sub-threshold winners while( !activeColumns.empty() && - overlaps[activeColumns.back()] < stimulusThreshold_) + overlaps[activeColumns.back()] < stimulusThreshold_) { activeColumns.pop_back(); + } + + activeColumns.shrink_to_fit(); + return activeColumns; } -void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, - Real density, - vector &activeColumns) const { - activeColumns.clear(); +vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, + const Real density) const { + NTA_ASSERT(overlaps.size() == numColumns_); + vector activeColumns; + //TODO reserve for numDesired // Tie-breaking: when overlaps are equal, columns that have already been // selected are treated as "bigger". @@ -913,7 +913,9 @@ void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenion const UInt numNeighbors = hood.size()-1; // -1 bcs "hood" includes the column itself (center) - const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); + //const UInt numActive = static_cast(ceil(density * (numNeighbors + 1))); + const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); + NTA_ASSERT(numActive > 0); //for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) P for (const auto neighbor: hood) { @@ -955,12 +957,16 @@ void SpatialPooler::inhibitColumnsLocal_(const vector &overlaps, } } const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); + // const UInt numActive = static_cast(ceil(density * (numNeighbors + 1))); + NTA_ASSERT(numActive > 0); + if (numBigger < numActive) { activeColumns.push_back(column); activeColumnsDense[column] = true; } } } + return activeColumns; } diff --git a/src/htm/algorithms/SpatialPooler.hpp b/src/htm/algorithms/SpatialPooler.hpp index 30e88e823e..67a64a7b91 100644 --- a/src/htm/algorithms/SpatialPooler.hpp +++ b/src/htm/algorithms/SpatialPooler.hpp @@ -858,11 +858,11 @@ class SpatialPooler : public Serializable in a "connected state" (connected synapses) that are connected to input bits which are turned on. - @param activeColumns an int array containing the indices of the active - columns. + @return activeColumns + a sparse SDR vector containing the indices of the active columns. + Internally delegates to local/global inhibition functions. */ - void inhibitColumns_(const vector &overlaps, - vector &activeColumns) const; + std::vector inhibitColumns_(const vector &overlaps) const; /** Perform global inhibition. @@ -882,11 +882,10 @@ class SpatialPooler : public Serializable @param density a real number of the fraction of columns to survive inhibition. - @param activeColumns - an int array containing the indices of the active columns. + @return activeColumns + an (sprase SDR) vector containing the indices of the active columns. */ - void inhibitColumnsGlobal_(const vector &overlaps, Real density, - vector &activeColumns) const; + std::vector inhibitColumnsGlobal_(const vector &overlaps, const Real density) const; /** Performs local inhibition. @@ -911,11 +910,10 @@ class SpatialPooler : public Serializable local fashion, the exact fraction of surviving columns is likely to vary. - @param activeColumns - an int array containing the indices of the active columns. + @return activeColumns + an (sparse SDR) vector containing the indices of the active columns. */ - void inhibitColumnsLocal_(const vector &overlaps, Real density, - vector &activeColumns) const; + std::vector inhibitColumnsLocal_(const vector &overlaps, const Real density) const; /** The primary method in charge of learning. diff --git a/src/test/unit/algorithms/SpatialPoolerTest.cpp b/src/test/unit/algorithms/SpatialPoolerTest.cpp index 0b02ffdf0f..f45be30724 100644 --- a/src/test/unit/algorithms/SpatialPoolerTest.cpp +++ b/src/test/unit/algorithms/SpatialPoolerTest.cpp @@ -1096,16 +1096,16 @@ TEST(SpatialPoolerTest, testInhibitColumns) { Real overlapsArray[10] = {10, 21, 34, 4, 18, 3, 12, 5, 7, 1}; overlapsReal.assign(&overlapsArray[0], &overlapsArray[numColumns]); - sp.inhibitColumnsGlobal_(overlapsReal, density, activeColumnsGlobal); + activeColumnsGlobal = sp.inhibitColumnsGlobal_(overlapsReal, density); overlapsReal.assign(&overlapsArray[0], &overlapsArray[numColumns]); - sp.inhibitColumnsLocal_(overlapsReal, density, activeColumnsLocal); + activeColumnsLocal = sp.inhibitColumnsLocal_(overlapsReal, density); sp.setInhibitionRadius(5); sp.setGlobalInhibition(true); sp.setLocalAreaDensity(density); overlaps.assign(&overlapsArray[0], &overlapsArray[numColumns]); - sp.inhibitColumns_(overlaps, activeColumns); + activeColumns = sp.inhibitColumns_(overlaps); ASSERT_TRUE(check_vector_eq(activeColumns, activeColumnsGlobal)); ASSERT_TRUE(!check_vector_eq(activeColumns, activeColumnsLocal)); @@ -1114,7 +1114,7 @@ TEST(SpatialPoolerTest, testInhibitColumns) { sp.setInhibitionRadius(numColumns + 1); overlaps.assign(&overlapsArray[0], &overlapsArray[numColumns]); - sp.inhibitColumns_(overlaps, activeColumns); + activeColumns = sp.inhibitColumns_(overlaps); ASSERT_TRUE(check_vector_eq(activeColumns, activeColumnsGlobal)); ASSERT_TRUE(!check_vector_eq(activeColumns, activeColumnsLocal)); @@ -1125,12 +1125,12 @@ TEST(SpatialPoolerTest, testInhibitColumns) { sp.setInhibitionRadius(inhibitionRadius); overlapsReal.assign(&overlapsArray[0], &overlapsArray[numColumns]); - sp.inhibitColumnsGlobal_(overlapsReal, density, activeColumnsGlobal); + activeColumnsGlobal = sp.inhibitColumnsGlobal_(overlapsReal, density); overlapsReal.assign(&overlapsArray[0], &overlapsArray[numColumns]); - sp.inhibitColumnsLocal_(overlapsReal, density, activeColumnsLocal); + activeColumnsLocal = sp.inhibitColumnsLocal_(overlapsReal, density); overlaps.assign(&overlapsArray[0], &overlapsArray[numColumns]); - sp.inhibitColumns_(overlaps, activeColumns); + activeColumns = sp.inhibitColumns_(overlaps); ASSERT_TRUE(!check_vector_eq(activeColumns, activeColumnsGlobal)); ASSERT_TRUE(check_vector_eq(activeColumns, activeColumnsLocal)); @@ -1151,7 +1151,7 @@ TEST(SpatialPoolerTest, testInhibitColumnsGlobal) { density = 0.3f; Real overlapsArray[10] = {1, 2, 1, 4, 8, 3, 12, 5, 4, 1}; overlaps.assign(&overlapsArray[0], &overlapsArray[numColumns]); - sp.inhibitColumnsGlobal_(overlaps, density, activeColumns); + activeColumns = sp.inhibitColumnsGlobal_(overlaps, density); UInt trueActiveArray1[3] = {4, 6, 7}; trueActive.assign(numColumns, 0); @@ -1170,7 +1170,7 @@ TEST(SpatialPoolerTest, testInhibitColumnsGlobal) { density = 0.5f; Real overlapsArray2[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; overlaps.assign(&overlapsArray2[0], &overlapsArray2[numColumns]); - sp.inhibitColumnsGlobal_(overlaps, density, activeColumns); + activeColumns = sp.inhibitColumnsGlobal_(overlaps, density); UInt trueActiveArray2[5] = {5, 6, 7, 8, 9}; for (auto &elem : trueActiveArray2) { @@ -1180,7 +1180,6 @@ TEST(SpatialPoolerTest, testInhibitColumnsGlobal) { for (auto &activeColumn : activeColumns) { active[activeColumn] = 1; } - ASSERT_TRUE(check_vector_eq(trueActive, active)); } @@ -1253,7 +1252,7 @@ TEST(SpatialPoolerTest, testInhibitColumnsLocal) { overlaps.assign(&overlapsArray1[0], &overlapsArray1[10]); UInt trueActive[5] = {1, 2, 5, 6, 9}; sp.setInhibitionRadius(inhibitionRadius); - sp.inhibitColumnsLocal_(overlaps, density, active); + active = sp.inhibitColumnsLocal_(overlaps, density); ASSERT_EQ(5ul, active.size()); ASSERT_TRUE(check_vector_eq(trueActive, active)); @@ -1264,8 +1263,8 @@ TEST(SpatialPoolerTest, testInhibitColumnsLocal) { inhibitionRadius = 3; density = 0.5; sp.setInhibitionRadius(inhibitionRadius); - sp.inhibitColumnsLocal_(overlaps, density, active); - ASSERT_TRUE(active.size() == 6); + active = sp.inhibitColumnsLocal_(overlaps, density); + ASSERT_EQ(active.size(), (size_t)6); ASSERT_TRUE(check_vector_eq(trueActive2, active)); // Test arbitration @@ -1277,9 +1276,10 @@ TEST(SpatialPoolerTest, testInhibitColumnsLocal) { inhibitionRadius = 3; density = 0.25; sp.setInhibitionRadius(inhibitionRadius); - sp.inhibitColumnsLocal_(overlaps, density, active); + active = sp.inhibitColumnsLocal_(overlaps, density); - ASSERT_TRUE(active.size() == 4); + for (auto a : active) std::cout << a << "\n"; + ASSERT_EQ(active.size(), (size_t)4); ASSERT_TRUE(check_vector_eq(trueActive3, active)); } @@ -1317,7 +1317,7 @@ TEST(SpatialPoolerTest, testInhibitColumnsLocal) { overlaps.assign(&overlapsArray1[0], &overlapsArray1[10]); UInt trueActive[6] = {1, 2, 5, 6, 8, 9}; sp.setInhibitionRadius(inhibitionRadius); - sp.inhibitColumnsLocal_(overlaps, density, active); + active = sp.inhibitColumnsLocal_(overlaps, density); ASSERT_EQ(6ul, active.size()); ASSERT_TRUE(check_vector_eq(trueActive, active)); @@ -1328,7 +1328,7 @@ TEST(SpatialPoolerTest, testInhibitColumnsLocal) { inhibitionRadius = 3; density = 0.5; sp.setInhibitionRadius(inhibitionRadius); - sp.inhibitColumnsLocal_(overlaps, density, active); + active = sp.inhibitColumnsLocal_(overlaps, density); ASSERT_TRUE(active.size() == 6); ASSERT_TRUE(check_vector_eq(trueActive2, active)); @@ -1341,7 +1341,7 @@ TEST(SpatialPoolerTest, testInhibitColumnsLocal) { inhibitionRadius = 3; density = 0.25; sp.setInhibitionRadius(inhibitionRadius); - sp.inhibitColumnsLocal_(overlaps, density, active); + active = sp.inhibitColumnsLocal_(overlaps, density); ASSERT_TRUE(active.size() == 4ul); ASSERT_TRUE(check_vector_eq(trueActive3, active)); From 1d3e86f3d84f7d8b8ed3d5ab8ca774dd2969cf34 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 14:26:45 +0100 Subject: [PATCH 09/43] final memory management touches --- src/htm/algorithms/SpatialPooler.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index e0f4dfc08b..6fd211b5e3 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -894,7 +894,10 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps const Real density) const { NTA_ASSERT(overlaps.size() == numColumns_); vector activeColumns; - //TODO reserve for numDesired + //optimization: reserve for numDesired approximation + const UInt approxNumDesired = static_cast(density * numColumns_); //note: this is just a heuristic, not precise number. It can be used for global inh, + //..but here the density is requested for inhibition radius. The guess is it correlates to the global somehow. + activeColumns.reserve(approxNumDesired); // Tie-breaking: when overlaps are equal, columns that have already been // selected are treated as "bigger". @@ -966,6 +969,7 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps } } } + activeColumns.shrink_to_fit(); return activeColumns; } From 551452996be46026e0cbfc7af27c05ec5d9db23a Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 14:45:28 +0100 Subject: [PATCH 10/43] Neighborhood: make params const --- src/htm/utils/Topology.cpp | 6 ++++-- src/htm/utils/Topology.hpp | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/htm/utils/Topology.cpp b/src/htm/utils/Topology.cpp index 50578c0196..14373410f2 100644 --- a/src/htm/utils/Topology.cpp +++ b/src/htm/utils/Topology.cpp @@ -136,7 +136,8 @@ UInt indexFromCoordinates(const vector &coordinates, // NEIGHBORHOOD // ============================================================================ -Neighborhood::Neighborhood(UInt centerIndex, UInt radius, +Neighborhood::Neighborhood(const UInt centerIndex, + const UInt radius, const vector &dimensions) : centerPosition_(coordinatesFromIndex(centerIndex, dimensions)), dimensions_(dimensions), radius_(radius) {} @@ -210,7 +211,8 @@ Neighborhood::Iterator Neighborhood::end() const { return {*this, true}; } // WRAPPING NEIGHBORHOOD // ============================================================================ -WrappingNeighborhood::WrappingNeighborhood(UInt centerIndex, UInt radius, +WrappingNeighborhood::WrappingNeighborhood(const UInt centerIndex, + const UInt radius, const vector &dimensions) : centerPosition_(coordinatesFromIndex(centerIndex, dimensions)), dimensions_(dimensions), radius_(radius) {} diff --git a/src/htm/utils/Topology.hpp b/src/htm/utils/Topology.hpp index 35ac443992..130ab8e9e8 100644 --- a/src/htm/utils/Topology.hpp +++ b/src/htm/utils/Topology.hpp @@ -192,7 +192,8 @@ UInt indexFromCoordinates(const std::vector &coordinates, */ class Neighborhood { public: - Neighborhood(UInt centerIndex, UInt radius, + Neighborhood(const UInt centerIndex, + const UInt radius, const std::vector &dimensions); class Iterator { @@ -241,7 +242,8 @@ class Neighborhood { */ class WrappingNeighborhood { public: - WrappingNeighborhood(UInt centerIndex, UInt radius, + WrappingNeighborhood(const UInt centerIndex, + const UInt radius, const std::vector &dimensions); class Iterator { From 17d58396a8724bae774825852968ac964a1e4bc6 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 16:39:51 +0100 Subject: [PATCH 11/43] Neighborhood: merged wrapping and non-wrap classes into common Neighborhood. Simplified code, all functionality still available with toggle wrap=true. --- src/htm/algorithms/SpatialPooler.cpp | 46 ++------- src/htm/utils/Topology.cpp | 145 +++++++-------------------- src/htm/utils/Topology.hpp | 58 ++--------- src/test/unit/math/TopologyTest.cpp | 4 +- 4 files changed, 58 insertions(+), 195 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 6fd211b5e3..169d113e08 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -486,27 +486,17 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea } -unordered_map> SpatialPooler::mapAllNeighbors() const { +unordered_map> SpatialPooler::mapAllNeighbors() const { //TODO move the cache logic to Neighbor class std::unordered_map> neighborMap; neighborMap.reserve(numColumns_); - - if (wrapAround_) { - for(UInt column=0; column < numColumns_; column++) { + for(UInt column=0; column < numColumns_; column++) { vector neighbors; //of the current column - for(const auto neighbor: WrappingNeighborhood(column, inhibitionRadius_, columnDimensions_)) { + for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_)) { neighbors.push_back(neighbor); } + neighbors.shrink_to_fit(); neighborMap[column] = neighbors; - } - } else { - for(UInt column=0; column < numColumns_; column++) { - vector neighbors; //of the current column - for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_)) { - neighbors.push_back(neighbor); - } - neighborMap[column] = neighbors; - } } return neighborMap; } @@ -548,18 +538,11 @@ vector SpatialPooler::initMapPotential_(UInt column, bool wrapAround) { const UInt centerInput = initMapColumn_(column); vector columnInputs; - if (wrapAround) { - for (UInt input : WrappingNeighborhood(centerInput, potentialRadius_, inputDimensions_)) { + for (UInt input : Neighborhood(centerInput, potentialRadius_, inputDimensions_, wrapAround)) { columnInputs.push_back(input); - } - } else { - for (UInt input : - Neighborhood(centerInput, potentialRadius_, inputDimensions_)) { - columnInputs.push_back(input); - } } - const UInt numPotential = (UInt)round(columnInputs.size() * potentialPct_); + const UInt numPotential = static_cast(round(columnInputs.size() * potentialPct_)); const auto selectedInputs = rng_.sample(columnInputs, numPotential); const vector potential = VectorHelpers::sparseToBinary(selectedInputs, numInputs_); return potential; @@ -640,16 +623,9 @@ void SpatialPooler::updateMinDutyCyclesLocal_() { for (UInt i = 0; i < numColumns_; i++) { Real maxActiveDuty = 0.0f; Real maxOverlapDuty = 0.0f; - if (wrapAround_) { - for(auto column : WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) { - maxActiveDuty = max(maxActiveDuty, activeDutyCycles_[column]); - maxOverlapDuty = max(maxOverlapDuty, overlapDutyCycles_[column]); - } - } else { - for(auto column: Neighborhood(i, inhibitionRadius_, columnDimensions_)) { + for(const auto column : Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_)) { maxActiveDuty = max(maxActiveDuty, activeDutyCycles_[column]); maxOverlapDuty = max(maxOverlapDuty, overlapDutyCycles_[column]); - } } minOverlapDutyCycles_[i] = maxOverlapDuty * minPctOverlapDutyCycles_; @@ -794,14 +770,14 @@ void SpatialPooler::updateBoostFactorsGlobal_() { void SpatialPooler::updateBoostFactorsLocal_() { - if (wrapAround_) { + if (wrapAround_) { //TODO merge the two IFs? for a small perf penalty? for (UInt i = 0; i < numColumns_; ++i) { Real localActivityDensity = 0.0f; const auto& hood = neighborMap_.at(i); //hood is vector<> of cached neighborhood values - //for(auto neighbor: WrappingNeighborhood(i, inhibitionRadius_, columnDimensions_)) P + //for(auto neighbor: Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_)) { for (const auto neighbor : hood) { localActivityDensity += activeDutyCycles_[neighbor]; } @@ -907,7 +883,7 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps if (wrapAround_) { for (UInt column = 0; column < numColumns_; column++) { - if (overlaps[column] < stimulusThreshold_) { + if (overlaps[column] < stimulusThreshold_) { //TODO make connections.computeActivity() already drop sub-threshold columns continue; } @@ -920,7 +896,7 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); NTA_ASSERT(numActive > 0); - //for(auto neighbor: WrappingNeighborhood(column, inhibitionRadius_,columnDimensions_)) P + //for(auto neighbor: Neighborhood(column, inhibitionRadius_,columnDimensions_, wrapAround_)) { for (const auto neighbor: hood) { if (neighbor == column) { continue; diff --git a/src/htm/utils/Topology.cpp b/src/htm/utils/Topology.cpp index 14373410f2..c4225bb10f 100644 --- a/src/htm/utils/Topology.cpp +++ b/src/htm/utils/Topology.cpp @@ -62,21 +62,12 @@ Topology_t DefaultTopology( const auto centerInput = inputTopologySDR.getSparse()[0]; vector columnInputs; - if( wrapAround ) { - for( UInt input : WrappingNeighborhood(centerInput, (UInt)floor(potentialRadius), inputTopology)) { + for( UInt input : Neighborhood(centerInput, (UInt)floor(potentialRadius), inputTopology, wrapAround /*wrapping*/)) { for( UInt extra = 0; extra < extraDimensions; ++extra ) { columnInputs.push_back( input * extraDimensions + extra ); } - } - } - else { - for( UInt input : - Neighborhood(centerInput, (UInt32)floor(potentialRadius), inputTopology)) { - for( UInt extra = 0; extra < extraDimensions; ++extra ) { - columnInputs.push_back( input * extraDimensions + extra ); - } - } } + const UInt numPotential = (UInt)round(columnInputs.size() * potentialPct); auto selectedInputs = rng.sample(columnInputs, numPotential); @@ -138,18 +129,21 @@ UInt indexFromCoordinates(const vector &coordinates, Neighborhood::Neighborhood(const UInt centerIndex, const UInt radius, - const vector &dimensions) + const vector &dimensions, + const bool wrap) : centerPosition_(coordinatesFromIndex(centerIndex, dimensions)), - dimensions_(dimensions), radius_(radius) {} + dimensions_(dimensions), radius_(radius), wrap_(wrap) {} Neighborhood::Iterator::Iterator(const Neighborhood &neighborhood, bool end) : neighborhood_(neighborhood), offset_(neighborhood.dimensions_.size(), -(Int)neighborhood.radius_), finished_(end) { - // Choose the first offset that has positive resulting coordinates. - for (size_t i = 0; i < offset_.size(); i++) { - offset_[i] = std::max(offset_[i], -(Int)neighborhood_.centerPosition_[i]); - } + if(!neighborhood.wrap_) { + // Choose the first offset that has positive resulting coordinates. + for (size_t i = 0; i < offset_.size(); i++) { + offset_[i] = std::max(offset_[i], -(Int)neighborhood_.centerPosition_[i]); + } + } } bool Neighborhood::Iterator::operator!=(const Iterator &other) const { @@ -159,7 +153,18 @@ bool Neighborhood::Iterator::operator!=(const Iterator &other) const { UInt Neighborhood::Iterator::operator*() const { UInt index = 0; for (size_t i = 0; i < neighborhood_.dimensions_.size(); i++) { - const Int coordinate = neighborhood_.centerPosition_[i] + offset_[i]; + Int coordinate = neighborhood_.centerPosition_[i] + offset_[i]; + + if(neighborhood_.wrap_) { + // With a large radius, it may have wrapped around multiple times, so use + // `while`, not `if`. + while (coordinate < 0) { //lower bound + coordinate += neighborhood_.dimensions_[i]; + } + while (coordinate >= static_cast(neighborhood_.dimensions_[i])) { //upper b + coordinate -= neighborhood_.dimensions_[i]; + } + } NTA_ASSERT(coordinate >= 0); NTA_ASSERT(coordinate < (Int)neighborhood_.dimensions_[i]); @@ -180,99 +185,28 @@ void Neighborhood::Iterator::advance_() { // When it overflows, we need to "carry the 1" to the next dimension. bool overflowed = true; - for (Int i = (Int)offset_.size() - 1; i >= 0; i--) { + for (Int i = static_cast(offset_.size()) - 1; i >= 0; i--) { offset_[i]++; - overflowed = offset_[i] > (Int)neighborhood_.radius_ || - (((Int)neighborhood_.centerPosition_[i] + offset_[i]) >= - (Int)neighborhood_.dimensions_[i]); - - if (overflowed) { - // Choose the first offset that has a positive resulting coordinate. - offset_[i] = std::max(-(Int)neighborhood_.radius_, - -(Int)neighborhood_.centerPosition_[i]); + if(!neighborhood_.wrap_) { + overflowed = offset_[i] > (Int)neighborhood_.radius_ || + (((Int)neighborhood_.centerPosition_[i] + offset_[i]) >= (Int)neighborhood_.dimensions_[i]); } else { - // There's no overflow. The remaining coordinates don't need to change. - break; - } - } - - // When the final coordinate overflows, we're done. - if (overflowed) { - finished_ = true; - } -} - -Neighborhood::Iterator Neighborhood::begin() const { return {*this, false}; } - -Neighborhood::Iterator Neighborhood::end() const { return {*this, true}; } - -// ============================================================================ -// WRAPPING NEIGHBORHOOD -// ============================================================================ - -WrappingNeighborhood::WrappingNeighborhood(const UInt centerIndex, - const UInt radius, - const vector &dimensions) - : centerPosition_(coordinatesFromIndex(centerIndex, dimensions)), - dimensions_(dimensions), radius_(radius) {} - -WrappingNeighborhood::Iterator::Iterator( - const WrappingNeighborhood &neighborhood, bool end) - : neighborhood_(neighborhood), - offset_(neighborhood.dimensions_.size(), -(Int)neighborhood.radius_), - finished_(end) {} - -bool WrappingNeighborhood::Iterator::operator!=(const Iterator &other) const { - return finished_ != other.finished_; -} - -UInt WrappingNeighborhood::Iterator::operator*() const { - UInt index = 0; - for (size_t i = 0; i < neighborhood_.dimensions_.size(); i++) { - Int coordinate = neighborhood_.centerPosition_[i] + offset_[i]; - - // With a large radius, it may have wrapped around multiple times, so use - // `while`, not `if`. - - while (coordinate < 0) { - coordinate += neighborhood_.dimensions_[i]; - } - - while (coordinate >= (Int)neighborhood_.dimensions_[i]) { - coordinate -= neighborhood_.dimensions_[i]; - } - - index *= neighborhood_.dimensions_[i]; - index += coordinate; - } - - return index; -} - -const WrappingNeighborhood::Iterator &WrappingNeighborhood::Iterator:: -operator++() { - advance_(); - return *this; -} - -void WrappingNeighborhood::Iterator::advance_() { - // When it overflows, we need to "carry the 1" to the next dimension. - bool overflowed = true; - - for (Int i = (Int)offset_.size() - 1; i >= 0; i--) { - offset_[i]++; - // If the offset has moved by more than the dimension size, i.e. if // offset_[i] - (-radius) is greater than the dimension size, then we're // about to run into points that we've already seen. This happens when given // small dimensions, a large radius, and wrap-around. overflowed = offset_[i] > (Int)neighborhood_.radius_ || - offset_[i] + (Int)neighborhood_.radius_ >= - (Int)neighborhood_.dimensions_[i]; + offset_[i] + (Int)neighborhood_.radius_ >= (Int)neighborhood_.dimensions_[i]; + } if (overflowed) { - offset_[i] = -(Int)neighborhood_.radius_; + if(!neighborhood_.wrap_) { + // Choose the first offset that has a positive resulting coordinate. + offset_[i] = std::max(-(Int)neighborhood_.radius_, -(Int)neighborhood_.centerPosition_[i]); + } else { + offset_[i] = -(Int)neighborhood_.radius_; + } } else { // There's no overflow. The remaining coordinates don't need to change. break; @@ -285,10 +219,5 @@ void WrappingNeighborhood::Iterator::advance_() { } } -WrappingNeighborhood::Iterator WrappingNeighborhood::begin() const { - return {*this, /*end*/ false}; -} - -WrappingNeighborhood::Iterator WrappingNeighborhood::end() const { - return {*this, /*end*/ true}; -} +Neighborhood::Iterator Neighborhood::begin() const { return {*this, false}; } +Neighborhood::Iterator Neighborhood::end() const { return {*this, true}; } diff --git a/src/htm/utils/Topology.hpp b/src/htm/utils/Topology.hpp index 130ab8e9e8..91e53462a3 100644 --- a/src/htm/utils/Topology.hpp +++ b/src/htm/utils/Topology.hpp @@ -184,6 +184,11 @@ UInt indexFromCoordinates(const std::vector &coordinates, * * @param dimensions * The dimensions of the world outside this neighborhood. + * + * @param wrap default false; + * If set to true: Like the Neighborhood class, except that the neighborhood isn't + * truncated when it's near an edge. It wraps around to the other side. (former + * WrappingNeighborhood class). * * @returns * An object which supports C++ range-based for loops. Each iteration of @@ -194,7 +199,8 @@ class Neighborhood { public: Neighborhood(const UInt centerIndex, const UInt radius, - const std::vector &dimensions); + const std::vector &dimensions, + const bool wrap = false); class Iterator { public: @@ -218,57 +224,9 @@ class Neighborhood { const std::vector centerPosition_; const std::vector &dimensions_; const UInt radius_; + const bool wrap_; }; -/** - * Like the Neighborhood class, except that the neighborhood isn't - * truncated when it's near an edge. It wraps around to the other side. - * - * @param centerIndex - * The center of this neighborhood. The coordinates are expressed as a - * single index by using the dimensions as a mixed radix definition. For - * example, in dimensions 42x10, the point [1, 4] is index 1*10 + 4 = 14. - * - * @param radius - * The radius of this neighborhood about the centerIndex. - * - * @param dimensions - * The dimensions of the world outside this neighborhood. - * - * @returns - * An object which supports C++ range-based for loops. Each iteration of - * the loop returns a point in the neighborhood. Each point is expressed - * as a single index. - */ -class WrappingNeighborhood { -public: - WrappingNeighborhood(const UInt centerIndex, - const UInt radius, - const std::vector &dimensions); - - class Iterator { - public: - Iterator(const WrappingNeighborhood &neighborhood, bool end); - bool operator!=(const Iterator &other) const; - UInt operator*() const; - const Iterator &operator++(); - - private: - void advance_(); - - const WrappingNeighborhood &neighborhood_; - std::vector offset_; - bool finished_; - }; - - Iterator begin() const; - Iterator end() const; - -private: - const std::vector centerPosition_; - const std::vector &dimensions_; - const UInt radius_; -}; } // end namespace htm diff --git a/src/test/unit/math/TopologyTest.cpp b/src/test/unit/math/TopologyTest.cpp index f732d8c770..fffb12ec9f 100644 --- a/src/test/unit/math/TopologyTest.cpp +++ b/src/test/unit/math/TopologyTest.cpp @@ -223,7 +223,7 @@ void expectWrappingNeighborhoodIndices(const vector ¢erCoords, const UInt centerIndex = indexFromCoordinates(centerCoords, dimensions); unsigned int i = 0u; - for (UInt index : WrappingNeighborhood(centerIndex, radius, dimensions)) { + for (UInt index : Neighborhood(centerIndex, radius, dimensions, /*wrap*/true)) { EXPECT_EQ(expected[i], index); i++; } @@ -238,7 +238,7 @@ void expectWrappingNeighborhoodCoords(const vector ¢erCoords, const UInt centerIndex = indexFromCoordinates(centerCoords, dimensions); unsigned int i = 0u; - for (UInt index : WrappingNeighborhood(centerIndex, radius, dimensions)) { + for (UInt index : Neighborhood(centerIndex, radius, dimensions, true)) { EXPECT_EQ(indexFromCoordinates(expected[i], dimensions), index); i++; } From 5fb1c3aaad61dc641a822265d9cf398f313226a4 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 16:56:27 +0100 Subject: [PATCH 12/43] SP merge updateBoostFactorsLocal wrap, non-wrap code into one. Small performance loss, cleaner code. --- src/htm/algorithms/SpatialPooler.cpp | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 169d113e08..ef16f7f027 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -770,37 +770,26 @@ void SpatialPooler::updateBoostFactorsGlobal_() { void SpatialPooler::updateBoostFactorsLocal_() { - if (wrapAround_) { //TODO merge the two IFs? for a small perf penalty? - for (UInt i = 0; i < numColumns_; ++i) { Real localActivityDensity = 0.0f; const auto& hood = neighborMap_.at(i); //hood is vector<> of cached neighborhood values + UInt numNeighbors = 0; //for(auto neighbor: Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_)) { for (const auto neighbor : hood) { localActivityDensity += activeDutyCycles_[neighbor]; + if(!wrapAround_) numNeighbors++; //update only for non-wrap, for wrap we compute it instantly } - // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, - // the number of dimensions, and of the size of each of those dimenions - const UInt numNeighbors = hood.size(); - const Real targetDensity = localActivityDensity / numNeighbors; - applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_); - } - } else { //non-wrap around //TODO remove, if wrapAround performance is consistently good? - for (UInt i = 0; i < numColumns_; ++i) { - UInt numNeighbors = 0u; - Real localActivityDensity = 0.0f; - const auto& hood = neighborMap_.at(i); //hood is vector<> of cached neighborhood values - - for(const auto neighbor: hood) { - localActivityDensity += activeDutyCycles_[neighbor]; - numNeighbors += 1; + if(wrapAround_) { + //optimization: In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, + // the number of dimensions, and of the size of each of those dimenions + numNeighbors = hood.size(); } + NTA_ASSERT(numNeighbors > 0); const Real targetDensity = localActivityDensity / numNeighbors; applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_); - } -} + } } From 9ac17c291bb17482b489e3971444d55c3b0903e2 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 18:12:26 +0100 Subject: [PATCH 13/43] SP: more use of cached hood --- src/htm/algorithms/SpatialPooler.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index ef16f7f027..0ff48d41f9 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -416,7 +416,7 @@ void SpatialPooler::initialize( dutyCyclePeriod_ = dutyCyclePeriod; boostStrength_ = boostStrength; spVerbosity_ = spVerbosity; - wrapAround_ = wrapAround; + wrapAround_ = wrapAround; //TODO consider keeping only wrapping version if results are the same (seems no difference), as wrap=true is much faster now for local inh. updatePeriod_ = 50u; initConnectedPct_ = 0.5f; //FIXME make SP's param, and much lower 0.01 https://discourse.numenta.org/t/spatial-pooler-implementation-for-mnist-dataset/2317/25?u=breznak iterationNum_ = 0u; @@ -623,7 +623,9 @@ void SpatialPooler::updateMinDutyCyclesLocal_() { for (UInt i = 0; i < numColumns_; i++) { Real maxActiveDuty = 0.0f; Real maxOverlapDuty = 0.0f; - for(const auto column : Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_)) { + const auto hood = neighborMap_[i]; + //for(const auto column : Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_)) { + for(const auto column : hood) { maxActiveDuty = max(maxActiveDuty, activeDutyCycles_[column]); maxOverlapDuty = max(maxOverlapDuty, overlapDutyCycles_[column]); } @@ -869,7 +871,7 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps vector activeColumnsDense(numColumns_, false); - if (wrapAround_) { + if (wrapAround_) { //TODO merge if not too big performance penalty for (UInt column = 0; column < numColumns_; column++) { if (overlaps[column] < stimulusThreshold_) { //TODO make connections.computeActivity() already drop sub-threshold columns From 9bb11d93c1438ed514523a521f516dd228620f79 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 18:29:10 +0100 Subject: [PATCH 14/43] move TopologyTest to correct location --- src/test/CMakeLists.txt | 10 +++------- src/test/unit/{math => utils}/TopologyTest.cpp | 0 2 files changed, 3 insertions(+), 7 deletions(-) rename src/test/unit/{math => utils}/TopologyTest.cpp (100%) diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 799f9a2db5..4f2d322cf2 100755 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -62,11 +62,6 @@ set(engine_tests unit/engine/WatcherTest.cpp ) - -set(math_tests - unit/math/TopologyTest.cpp - ) - set(ntypes_tests unit/ntypes/ArrayTest.cpp unit/ntypes/BasicTypeTest.cpp @@ -90,8 +85,8 @@ set(regions_tests unit/regions/ScalarSensorTest.cpp unit/regions/RDSERegionTest.cpp unit/regions/SPRegionTest.cpp - unit/regions/TMRegionTest.cpp - unit/regions/VectorFileTest.cpp + unit/regions/TMRegionTest.cpp + unit/regions/VectorFileTest.cpp ) @@ -106,6 +101,7 @@ set(utils_tests unit/utils/RandomTest.cpp unit/utils/VectorHelpersTest.cpp unit/utils/SdrMetricsTest.cpp + unit/utils/TopologyTest.cpp ) set(examples_files diff --git a/src/test/unit/math/TopologyTest.cpp b/src/test/unit/utils/TopologyTest.cpp similarity index 100% rename from src/test/unit/math/TopologyTest.cpp rename to src/test/unit/utils/TopologyTest.cpp From a0985e33d934e0e3e9a0283ca534d7eaf534fd71 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 19:32:13 +0100 Subject: [PATCH 15/43] Neighborhood: warn that non-wrapping is slow --- src/htm/utils/Topology.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/htm/utils/Topology.cpp b/src/htm/utils/Topology.cpp index c4225bb10f..40c5af5add 100644 --- a/src/htm/utils/Topology.cpp +++ b/src/htm/utils/Topology.cpp @@ -132,7 +132,11 @@ Neighborhood::Neighborhood(const UInt centerIndex, const vector &dimensions, const bool wrap) : centerPosition_(coordinatesFromIndex(centerIndex, dimensions)), - dimensions_(dimensions), radius_(radius), wrap_(wrap) {} + dimensions_(dimensions), radius_(radius), wrap_(wrap) { + if(wrap == false) { + NTA_WARN << "Neighborhood uses wrap=false which runs considerably slower with local inhibition!"; + } + } Neighborhood::Iterator::Iterator(const Neighborhood &neighborhood, bool end) : neighborhood_(neighborhood), From 87446ba5d237aca7567f779c8453cbe9acbba781 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 22:06:21 +0100 Subject: [PATCH 16/43] Neighborhood: add option to skipCenter which can save a check in hot-loop in SP (as center is excluded). --- src/htm/utils/Topology.cpp | 14 +++++++++++--- src/htm/utils/Topology.hpp | 10 ++++++++-- src/test/unit/utils/TopologyTest.cpp | 23 +++++++++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/htm/utils/Topology.cpp b/src/htm/utils/Topology.cpp index 40c5af5add..e255d352a3 100644 --- a/src/htm/utils/Topology.cpp +++ b/src/htm/utils/Topology.cpp @@ -130,9 +130,10 @@ UInt indexFromCoordinates(const vector &coordinates, Neighborhood::Neighborhood(const UInt centerIndex, const UInt radius, const vector &dimensions, - const bool wrap) + const bool wrap, + const bool skipCenter) : centerPosition_(coordinatesFromIndex(centerIndex, dimensions)), - dimensions_(dimensions), radius_(radius), wrap_(wrap) { + dimensions_(dimensions), radius_(radius), wrap_(wrap), skipCenter_(skipCenter), center_(centerIndex) { if(wrap == false) { NTA_WARN << "Neighborhood uses wrap=false which runs considerably slower with local inhibition!"; } @@ -154,8 +155,10 @@ bool Neighborhood::Iterator::operator!=(const Iterator &other) const { return finished_ != other.finished_; } -UInt Neighborhood::Iterator::operator*() const { +UInt Neighborhood::Iterator::operator*() { UInt index = 0; + + NTA_ASSERT(neighborhood_.dimensions_.size() == offset_.size() and offset_.size() == neighborhood_.centerPosition_.size()); for (size_t i = 0; i < neighborhood_.dimensions_.size(); i++) { Int coordinate = neighborhood_.centerPosition_[i] + offset_[i]; @@ -177,6 +180,11 @@ UInt Neighborhood::Iterator::operator*() const { index += coordinate; } + if(neighborhood_.skipCenter_ and index == neighborhood_.center_) { + advance_(); + return this->operator*(); + } + return index; } diff --git a/src/htm/utils/Topology.hpp b/src/htm/utils/Topology.hpp index 91e53462a3..d2406dd9e3 100644 --- a/src/htm/utils/Topology.hpp +++ b/src/htm/utils/Topology.hpp @@ -189,6 +189,9 @@ UInt indexFromCoordinates(const std::vector &coordinates, * If set to true: Like the Neighborhood class, except that the neighborhood isn't * truncated when it's near an edge. It wraps around to the other side. (former * WrappingNeighborhood class). + * + * @param skipSelf (default false) skips centerIndex from the list of neighbors. + * Used eg. in SpatialPooler to avoid (an extra) IF in a heated loop. * * @returns * An object which supports C++ range-based for loops. Each iteration of @@ -200,13 +203,14 @@ class Neighborhood { Neighborhood(const UInt centerIndex, const UInt radius, const std::vector &dimensions, - const bool wrap = false); + const bool wrap = false, + const bool skipCenter = false); class Iterator { public: Iterator(const Neighborhood &neighborhood, bool end); bool operator!=(const Iterator &other) const; - UInt operator*() const; + UInt operator*(); const Iterator &operator++(); private: @@ -225,6 +229,8 @@ class Neighborhood { const std::vector &dimensions_; const UInt radius_; const bool wrap_; + const bool skipCenter_; + const UInt center_; //the idx of self/center column }; diff --git a/src/test/unit/utils/TopologyTest.cpp b/src/test/unit/utils/TopologyTest.cpp index fffb12ec9f..de90586afb 100644 --- a/src/test/unit/utils/TopologyTest.cpp +++ b/src/test/unit/utils/TopologyTest.cpp @@ -370,4 +370,27 @@ TEST(TopologyTest, WrappingNeighborhoodDimensionOne) { /*radius*/ 1, /*expected*/ {{4, 0, 0}, {5, 0, 0}, {6, 0, 0}}); } + +TEST(TopologyTest, NeighborhoodSkipCenter) { +const UInt center = 2; +const UInt radius = 3; +const vector dims = {10, 15}; + +bool skipCenter = true; +for(const auto i: Neighborhood(center, radius, dims, /*wrap*/ false, /*skip center=*/ skipCenter )) { + ASSERT_NE(center, i) << "Center should be skipped!"; +} + +skipCenter = false; +bool centerFound_ = false; +for(const auto i: Neighborhood(center, radius, dims, /*wrap*/ false, /*skip center=*/ skipCenter )) { + if(i == center) { + centerFound_ = true; + break; + } +} +ASSERT_TRUE(centerFound_) << "Center was not included in hood!"; + +} + } // namespace From ce76ba3e96c42422b276b32a6d318581f652c81e Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Wed, 18 Mar 2020 23:08:32 +0100 Subject: [PATCH 17/43] SP formatting --- src/htm/algorithms/SpatialPooler.cpp | 70 +++++++++++++--------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 0ff48d41f9..df85482029 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -492,7 +492,7 @@ unordered_map> SpatialPooler::mapAllNeighbors() const { for(UInt column=0; column < numColumns_; column++) { vector neighbors; //of the current column - for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_)) { + for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_, false /*skip center*/)) { neighbors.push_back(neighbor); } neighbors.shrink_to_fit(); @@ -538,7 +538,7 @@ vector SpatialPooler::initMapPotential_(UInt column, bool wrapAround) { const UInt centerInput = initMapColumn_(column); vector columnInputs; - for (UInt input : Neighborhood(centerInput, potentialRadius_, inputDimensions_, wrapAround)) { + for (UInt input : Neighborhood(centerInput, potentialRadius_, inputDimensions_, wrapAround, false /*=include center*/)) { columnInputs.push_back(input); } @@ -887,53 +887,49 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); NTA_ASSERT(numActive > 0); - //for(auto neighbor: Neighborhood(column, inhibitionRadius_,columnDimensions_, wrapAround_)) { + //for(auto neighbor: Neighborhood(column, inhibitionRadius_,columnDimensions_, wrapAround_, false /*skip center*/)) { for (const auto neighbor: hood) { - if (neighbor == column) { - continue; - } - - const Real difference = overlaps[neighbor] - overlaps[column]; - if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { - numBigger++; - if (numBigger >= numActive) { break; } - } - } - if (numBigger < numActive) { - activeColumns.push_back(column); - activeColumnsDense[column] = true; + if(column == neighbor) continue; //TODO avoid this! + NTA_ASSERT(neighbor != column); + + const Real difference = overlaps[neighbor] - overlaps[column]; + if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { + numBigger++; + if (numBigger >= numActive) { break; } } + } + if (numBigger < numActive) { + activeColumns.push_back(column); + activeColumnsDense[column] = true; + } } } else { - for (UInt column = 0; column < numColumns_; column++) { - if (overlaps[column] < stimulusThreshold_) { - continue; - } + for (UInt column = 0; column < numColumns_; column++) { + if (overlaps[column] < stimulusThreshold_) continue; UInt numNeighbors = 0; UInt numBigger = 0; + const auto& hood = neighborMap_.at(column); + for(auto neighbor: hood) { + if(column == neighbor) continue; //TODO avoid this skip + NTA_ASSERT(neighbor != column); + numNeighbors++; - for(auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_)) { - if (neighbor == column) { - continue; - } - numNeighbors++; - - const Real difference = overlaps[neighbor] - overlaps[column]; - if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { - numBigger++; - } + const Real difference = overlaps[neighbor] - overlaps[column]; + if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { + numBigger++; } - const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); - // const UInt numActive = static_cast(ceil(density * (numNeighbors + 1))); - NTA_ASSERT(numActive > 0); + } + const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); + // const UInt numActive = static_cast(ceil(density * (numNeighbors + 1))); + NTA_ASSERT(numActive > 0); - if (numBigger < numActive) { - activeColumns.push_back(column); - activeColumnsDense[column] = true; - } + if (numBigger < numActive) { + activeColumns.push_back(column); + activeColumnsDense[column] = true; + } } } activeColumns.shrink_to_fit(); From 283c3e62db7f250fe75290b9efc3e4d5d3a52206 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 19 Mar 2020 02:25:24 +0100 Subject: [PATCH 18/43] Test SP: fix typo in comparison for local inh, "test 4" was not run at all. --- src/test/unit/algorithms/SpatialPoolerTest.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/unit/algorithms/SpatialPoolerTest.cpp b/src/test/unit/algorithms/SpatialPoolerTest.cpp index f45be30724..593edf60dc 100644 --- a/src/test/unit/algorithms/SpatialPoolerTest.cpp +++ b/src/test/unit/algorithms/SpatialPoolerTest.cpp @@ -971,6 +971,7 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { sp.setActiveDutyCycles(initActiveDutyCycles1); sp.updateBoostFactors_(); sp.getBoostFactors(resultBoostFactors1.data()); + ASSERT_EQ(resultBoostFactors1.size(), trueBoostFactors1.size()); ASSERT_TRUE(check_vector_eq(trueBoostFactors1, resultBoostFactors1)); Real32 initActiveDutyCycles2[] = {0.1f, 0.3f, 0.02f, 0.04f, 0.7f, 0.12f}; @@ -984,7 +985,7 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { sp.setActiveDutyCycles(initActiveDutyCycles2); sp.updateBoostFactors_(); sp.getBoostFactors(resultBoostFactors2.data()); - + ASSERT_EQ(resultBoostFactors2.size(), trueBoostFactors2.size()); ASSERT_TRUE(check_vector_eq(trueBoostFactors2, resultBoostFactors2)); Real32 initActiveDutyCycles3[] = {0.1f, 0.3f, 0.02f, 0.04f, 0.7f, 0.12f}; @@ -1000,7 +1001,7 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { sp.setActiveDutyCycles(initActiveDutyCycles3); sp.updateBoostFactors_(); sp.getBoostFactors(resultBoostFactors3.data()); - + ASSERT_EQ(resultBoostFactors3.size(), trueBoostFactors3.size()); ASSERT_TRUE(check_vector_eq(trueBoostFactors3, resultBoostFactors3)); Real32 initActiveDutyCycles4[] = {0.1f, 0.3f, 0.02f, 0.04f, 0.7f, 0.12f}; @@ -1016,7 +1017,7 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { sp.updateBoostFactors_(); sp.getBoostFactors(resultBoostFactors4.data()); - ASSERT_TRUE(check_vector_eq(trueBoostFactors3, resultBoostFactors3)); + ASSERT_TRUE(check_vector_eq(trueBoostFactors4, resultBoostFactors4)); } From d78014315343ca6b05c9be51fc5628ed1122d2a8 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 19 Mar 2020 10:49:25 +0100 Subject: [PATCH 19/43] SP: updateMinDutyCyclesLocal avoid unnecessary access. optimization. --- src/htm/algorithms/SpatialPooler.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index df85482029..2e3f34d42e 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -621,10 +621,9 @@ void SpatialPooler::updateMinDutyCyclesGlobal_() { void SpatialPooler::updateMinDutyCyclesLocal_() { for (UInt i = 0; i < numColumns_; i++) { - Real maxActiveDuty = 0.0f; - Real maxOverlapDuty = 0.0f; - const auto hood = neighborMap_[i]; - //for(const auto column : Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_)) { + Real maxOverlapDuty = overlapDutyCycles_[i]; //start with the center, which is column 'i' + const auto& hood = neighborMap_[i]; + //for(const auto column : Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_, /*skip center=*/false)) { for(const auto column : hood) { maxActiveDuty = max(maxActiveDuty, activeDutyCycles_[column]); maxOverlapDuty = max(maxOverlapDuty, overlapDutyCycles_[column]); From 8e70e23048a9a56aaa61c4fcecce15ccef3a7ddb Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 19 Mar 2020 11:26:46 +0100 Subject: [PATCH 20/43] SP cache: sort neighbors for better mem locality --- src/htm/algorithms/SpatialPooler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 2e3f34d42e..739dfcc782 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -495,6 +495,7 @@ unordered_map> SpatialPooler::mapAllNeighbors() const { for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_, false /*skip center*/)) { neighbors.push_back(neighbor); } + std::sort(neighbors.begin(), neighbors.end()); //sort for better cache locality neighbors.shrink_to_fit(); neighborMap[column] = neighbors; } From 45dccb540d4fd1cddaf880482ffb4bf4f3a4f4ff Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 19 Mar 2020 11:52:46 +0100 Subject: [PATCH 21/43] fix for d780143 --- src/htm/algorithms/SpatialPooler.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 739dfcc782..d1373eed98 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -626,10 +626,8 @@ void SpatialPooler::updateMinDutyCyclesLocal_() { const auto& hood = neighborMap_[i]; //for(const auto column : Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_, /*skip center=*/false)) { for(const auto column : hood) { - maxActiveDuty = max(maxActiveDuty, activeDutyCycles_[column]); maxOverlapDuty = max(maxOverlapDuty, overlapDutyCycles_[column]); } - minOverlapDutyCycles_[i] = maxOverlapDuty * minPctOverlapDutyCycles_; } } From 23f94f2007170c393bb0ee575823315473a5f56c Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 19 Mar 2020 12:20:14 +0100 Subject: [PATCH 22/43] SP updateBoostFactorsLocal for both wrap, non-wrap optimized --- src/htm/algorithms/SpatialPooler.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index d1373eed98..7ea4de2c6d 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -772,24 +772,27 @@ void SpatialPooler::updateBoostFactorsGlobal_() { void SpatialPooler::updateBoostFactorsLocal_() { for (UInt i = 0; i < numColumns_; ++i) { Real localActivityDensity = 0.0f; - - const auto& hood = neighborMap_.at(i); //hood is vector<> of cached neighborhood values - UInt numNeighbors = 0; + const auto& hood = neighborMap_[i]; //hood is vector<> of cached neighborhood values + const bool centerIncluded = std::find(hood.cbegin(), hood.cend(), i) != hood.cend(); //TODO avoid this once hood w/o center works for SP! + //optimization: In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, + // the number of dimensions, and of the size of each of those dimenions. + // Or in non-wrap, if we use cached hood, we obtain the value the same as hood.size() + const UInt numNeighbors = hood.size() + (centerIncluded ? 0 : 1); + NTA_ASSERT(numNeighbors > 0); + if (! centerIncluded) { + //start by adding the center ('i') which is not included in the hood + localActivityDensity += activeDutyCycles_[i]; //include the center, which is 'i' (not included in hood) + } + //for(auto neighbor: Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_)) { for (const auto neighbor : hood) { localActivityDensity += activeDutyCycles_[neighbor]; - if(!wrapAround_) numNeighbors++; //update only for non-wrap, for wrap we compute it instantly + //numNeighbors++; } - if(wrapAround_) { - //optimization: In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, - // the number of dimensions, and of the size of each of those dimenions - numNeighbors = hood.size(); - } - NTA_ASSERT(numNeighbors > 0); const Real targetDensity = localActivityDensity / numNeighbors; applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_); - } + } } From 4fe6d0b7dbe4b96b8dafdc6856b27115dabeb19c Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 19 Mar 2020 12:24:52 +0100 Subject: [PATCH 23/43] SP inhibit Local rename variables to better descriptive --- src/htm/algorithms/SpatialPooler.cpp | 45 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 7ea4de2c6d..62e4927bc3 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -869,7 +869,7 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps // Tie-breaking: when overlaps are equal, columns that have already been // selected are treated as "bigger". - vector activeColumnsDense(numColumns_, false); + vector alreadyUsedColumn(numColumns_, false); // in tie we prefer already used columns if (wrapAround_) { //TODO merge if not too big performance penalty @@ -879,29 +879,31 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps continue; } - UInt numBigger = 0; + UInt otherBigger = 0; //how many neighbor columns are bigger/better than this column 'column'. + //..aka. how many times this column lost. + const auto& hood = neighborMap_.at(column); // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenion - const UInt numNeighbors = hood.size()-1; // -1 bcs "hood" includes the column itself (center) - //const UInt numActive = static_cast(ceil(density * (numNeighbors + 1))); - const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); - NTA_ASSERT(numActive > 0); + const UInt numNeighbors = hood.size(); // -1 if "hood" includes the column itself (center); See #also2 + //const UInt numDesiredLocalActive = static_cast(ceil(density * (numNeighbors + 1))); + const UInt numDesiredLocalActive = static_cast(0.5f + (density * (numNeighbors + 1))); + NTA_ASSERT(numDesiredLocalActive > 0); //for(auto neighbor: Neighborhood(column, inhibitionRadius_,columnDimensions_, wrapAround_, false /*skip center*/)) { for (const auto neighbor: hood) { if(column == neighbor) continue; //TODO avoid this! NTA_ASSERT(neighbor != column); - const Real difference = overlaps[neighbor] - overlaps[column]; - if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { - numBigger++; - if (numBigger >= numActive) { break; } + if (overlaps[neighbor] > overlaps[column] || ( (overlaps[neighbor] == overlaps[column]) && alreadyUsedColumn[neighbor])) { //this column lost to a neighbor + otherBigger++; + if (otherBigger >= numDesiredLocalActive) { break; } } } - if (numBigger < numActive) { + + if (otherBigger < numDesiredLocalActive) { //successful column, add it activeColumns.push_back(column); - activeColumnsDense[column] = true; + alreadyUsedColumn[column] = true; } } @@ -909,27 +911,26 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps for (UInt column = 0; column < numColumns_; column++) { if (overlaps[column] < stimulusThreshold_) continue; - UInt numNeighbors = 0; - UInt numBigger = 0; + UInt otherBigger = 0; const auto& hood = neighborMap_.at(column); + const UInt numNeighbors = hood.size(); for(auto neighbor: hood) { if(column == neighbor) continue; //TODO avoid this skip NTA_ASSERT(neighbor != column); - numNeighbors++; const Real difference = overlaps[neighbor] - overlaps[column]; - if (difference > 0 || (difference == 0 && activeColumnsDense[neighbor])) { - numBigger++; + if (overlaps[neighbor] > overlaps[column] || ((overlaps[neighbor] == overlaps[column]) && alreadyUsedColumn[neighbor])) { + otherBigger++; } } - const UInt numActive = static_cast(0.5f + (density * (numNeighbors + 1))); - // const UInt numActive = static_cast(ceil(density * (numNeighbors + 1))); - NTA_ASSERT(numActive > 0); + const UInt numDesiredLocalActive = static_cast(0.5f + (density * (numNeighbors + 1))); + // const UInt numDesiredLocalActive = static_cast(ceil(density * (numNeighbors + 1))); + NTA_ASSERT(numDesiredLocalActive > 0); - if (numBigger < numActive) { + if (otherBigger < numDesiredLocalActive) { activeColumns.push_back(column); - activeColumnsDense[column] = true; + alreadyUsedColumn[column] = true; } } } From bdb349421b9651775b0bbbaffc56f7de148faec1 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 19 Mar 2020 12:35:31 +0100 Subject: [PATCH 24/43] SP local inh: merged wrap, non-wrap code together --- src/htm/algorithms/SpatialPooler.cpp | 35 ++-------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 62e4927bc3..8718e48b2b 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -871,9 +871,6 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps // selected are treated as "bigger". vector alreadyUsedColumn(numColumns_, false); // in tie we prefer already used columns - - if (wrapAround_) { //TODO merge if not too big performance penalty - for (UInt column = 0; column < numColumns_; column++) { if (overlaps[column] < stimulusThreshold_) { //TODO make connections.computeActivity() already drop sub-threshold columns continue; @@ -882,10 +879,10 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps UInt otherBigger = 0; //how many neighbor columns are bigger/better than this column 'column'. //..aka. how many times this column lost. - const auto& hood = neighborMap_.at(column); + const auto& hood = neighborMap_[column]; // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenion - const UInt numNeighbors = hood.size(); // -1 if "hood" includes the column itself (center); See #also2 + const UInt numNeighbors = hood.size(); // -1 if "hood" includes the column itself (center); See #also2 //TODO add check for w / w/o center //const UInt numDesiredLocalActive = static_cast(ceil(density * (numNeighbors + 1))); const UInt numDesiredLocalActive = static_cast(0.5f + (density * (numNeighbors + 1))); NTA_ASSERT(numDesiredLocalActive > 0); @@ -906,34 +903,6 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps alreadyUsedColumn[column] = true; } } - - } else { - for (UInt column = 0; column < numColumns_; column++) { - if (overlaps[column] < stimulusThreshold_) continue; - - UInt otherBigger = 0; - const auto& hood = neighborMap_.at(column); - const UInt numNeighbors = hood.size(); - - for(auto neighbor: hood) { - if(column == neighbor) continue; //TODO avoid this skip - NTA_ASSERT(neighbor != column); - - const Real difference = overlaps[neighbor] - overlaps[column]; - if (overlaps[neighbor] > overlaps[column] || ((overlaps[neighbor] == overlaps[column]) && alreadyUsedColumn[neighbor])) { - otherBigger++; - } - } - const UInt numDesiredLocalActive = static_cast(0.5f + (density * (numNeighbors + 1))); - // const UInt numDesiredLocalActive = static_cast(ceil(density * (numNeighbors + 1))); - NTA_ASSERT(numDesiredLocalActive > 0); - - if (otherBigger < numDesiredLocalActive) { - activeColumns.push_back(column); - alreadyUsedColumn[column] = true; - } - } - } activeColumns.shrink_to_fit(); return activeColumns; } From 57c67c8837dc81a5d67412ffb1de889e69fea6ad Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Fri, 10 Apr 2020 17:06:45 +0200 Subject: [PATCH 25/43] WIP --- src/htm/algorithms/SpatialPooler.cpp | 11 ++++++++--- src/test/unit/algorithms/SpatialPoolerTest.cpp | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 8718e48b2b..0ba84a5b30 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -780,6 +780,7 @@ void SpatialPooler::updateBoostFactorsLocal_() { // Or in non-wrap, if we use cached hood, we obtain the value the same as hood.size() const UInt numNeighbors = hood.size() + (centerIncluded ? 0 : 1); NTA_ASSERT(numNeighbors > 0); + NTA_CHECK(centerIncluded); if (! centerIncluded) { //start by adding the center ('i') which is not included in the hood localActivityDensity += activeDutyCycles_[i]; //include the center, which is 'i' (not included in hood) @@ -870,7 +871,10 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps // Tie-breaking: when overlaps are equal, columns that have already been // selected are treated as "bigger". vector alreadyUsedColumn(numColumns_, false); // in tie we prefer already used columns - + + const bool centerIncluded = std::find(neighborMap_.at(0).cbegin(), neighborMap_.at(0).cend(), 0) != neighborMap_.at(0).cend(); //TODO remove this if SP uses only hood w/o center + std::cout << "CENTER=" << centerIncluded << "\n"; + for (UInt column = 0; column < numColumns_; column++) { if (overlaps[column] < stimulusThreshold_) { //TODO make connections.computeActivity() already drop sub-threshold columns continue; @@ -879,10 +883,11 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps UInt otherBigger = 0; //how many neighbor columns are bigger/better than this column 'column'. //..aka. how many times this column lost. - const auto& hood = neighborMap_[column]; + const auto& hood = neighborMap_.at(column); // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenion - const UInt numNeighbors = hood.size(); // -1 if "hood" includes the column itself (center); See #also2 //TODO add check for w / w/o center + const UInt numNeighbors = hood.size() + centerIncluded ? -1 : 0; // +1 if "hood" does not includes the column itself (center); See #also2 + NTA_ASSERT(numNeighbors >= 0); //const UInt numDesiredLocalActive = static_cast(ceil(density * (numNeighbors + 1))); const UInt numDesiredLocalActive = static_cast(0.5f + (density * (numNeighbors + 1))); NTA_ASSERT(numDesiredLocalActive > 0); diff --git a/src/test/unit/algorithms/SpatialPoolerTest.cpp b/src/test/unit/algorithms/SpatialPoolerTest.cpp index 593edf60dc..9e63d836ba 100644 --- a/src/test/unit/algorithms/SpatialPoolerTest.cpp +++ b/src/test/unit/algorithms/SpatialPoolerTest.cpp @@ -1761,8 +1761,8 @@ TEST(SpatialPoolerTest, ZeroOverlap_StimulusThreshold_GlobalInhibition) { TEST(SpatialPoolerTest, ZeroOverlap_NoStimulusThreshold_LocalInhibition) { - const UInt inputSize = 10; - const UInt nColumns = 20; + const UInt inputSize = 100; + const UInt nColumns = 200; SpatialPooler sp({inputSize}, {nColumns}, /*potentialRadius*/ 5, @@ -1789,8 +1789,8 @@ TEST(SpatialPoolerTest, ZeroOverlap_NoStimulusThreshold_LocalInhibition) { TEST(SpatialPoolerTest, ZeroOverlap_StimulusThreshold_LocalInhibition) { - const UInt inputSize = 10; - const UInt nColumns = 20; + const UInt inputSize = 100; + const UInt nColumns = 200; SpatialPooler sp({inputSize}, {nColumns}, /*potentialRadius*/ 10, From 51fa4b8f8d94148b9d0322eef598b9b3d3ec9dd0 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 4 Jun 2020 12:01:51 +0200 Subject: [PATCH 26/43] another brakets fix --- src/htm/algorithms/SpatialPooler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 52c2791ecb..975cb2f214 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -883,7 +883,7 @@ vector SpatialPooler::inhibitColumnsGlobal_(const vector &overlap // Compare the column indexes by their overlap. auto compare = [&overlaps](const UInt &a, const UInt &b) -> bool - {return (overlaps[a] == overlaps[b]) ? a > b : overlaps[a] > overlaps[b];}; //for determinism if overlaps match (tieBreaker does not solve that), + {return (overlaps[a] == overlaps[b]) ? (a > b) : (overlaps[a] > overlaps[b]) ;}; //for determinism if overlaps match (tieBreaker does not solve that), //otherwise we'd return just `return overlaps[a] > overlaps[b]`. // Do a partial sort to divide the winners from the losers. This sort is From d921e7dc8807fd46b5b3af2a63686860aaf0d4d0 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 4 Jun 2020 12:11:46 +0200 Subject: [PATCH 27/43] fixes after merge conflicts, for py bindings --- bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp | 2 +- bindings/py/cpp_src/bindings/math/py_Topology.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp b/bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp index 8a281f8acf..b509bf6faf 100644 --- a/bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp +++ b/bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp @@ -408,7 +408,7 @@ Argument output An SDR representing the winning columns after { std::vector overlapsVector(get_it(overlaps), get_end(overlaps)); std::vector activeColumnsVector; - self.inhibitColumns_(overlapsVector, activeColumnsVector); + self.inhibitColumns_(overlapsVector); return py::array_t( activeColumnsVector.size(), activeColumnsVector.data()); }; diff --git a/bindings/py/cpp_src/bindings/math/py_Topology.cpp b/bindings/py/cpp_src/bindings/math/py_Topology.cpp index 5811608c7a..c205298e93 100644 --- a/bindings/py/cpp_src/bindings/math/py_Topology.cpp +++ b/bindings/py/cpp_src/bindings/math/py_Topology.cpp @@ -130,7 +130,7 @@ as a single index.)"); const std::vector &dimensions) { vector neighbors; - for( auto idx : WrappingNeighborhood( centerIndex, radius, dimensions )) { + for( auto idx : Neighborhood( centerIndex, radius, dimensions, /*wrap=*/true )) { neighbors.push_back( idx ); } return neighbors; From 690014d2aed6f9ebcc241ec9e2bc12b411219b81 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 4 Jun 2020 12:30:26 +0200 Subject: [PATCH 28/43] SP work on hood with centerIncluded logic --- src/htm/algorithms/SpatialPooler.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 975cb2f214..59d9ff5330 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -508,7 +508,7 @@ unordered_map> SpatialPooler::mapAllNeighbors() const { for(UInt column=0; column < numColumns_; column++) { vector neighbors; //of the current column - for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_, false /*skip center*/)) { + for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_, /*skip_center=*/false)) { neighbors.push_back(neighbor); } std::sort(neighbors.begin(), neighbors.end()); //sort for better cache locality @@ -555,7 +555,7 @@ vector SpatialPooler::initMapPotential_(UInt column, bool wrapAround) { const UInt centerInput = initMapColumn_(column); vector columnInputs; - for (const auto input : Neighborhood(centerInput, potentialRadius_, inputDimensions_, wrapAround, false /*=include center*/)) { + for (const auto input : Neighborhood(centerInput, potentialRadius_, inputDimensions_, wrapAround, /*skip_center=*/false)) { columnInputs.push_back(input); } @@ -800,13 +800,12 @@ void SpatialPooler::updateBoostFactorsLocal_() { Real localActivityDensity = 0.0f; const auto& hood = neighborMap_[i]; //hood is vector<> of cached neighborhood values - const bool centerIncluded = std::find(hood.cbegin(), hood.cend(), i) != hood.cend(); //TODO avoid this once hood w/o center works for SP! //optimization: In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenions. // Or in non-wrap, if we use cached hood, we obtain the value the same as hood.size() + const bool centerIncluded = std::find(hood.cbegin(), hood.cend(), i) != hood.cend(); //TODO avoid this once hood w/o center works for SP! const UInt numNeighbors = hood.size() + (centerIncluded ? 0 : 1); NTA_ASSERT(numNeighbors > 0); - NTA_CHECK(centerIncluded); if (! centerIncluded) { //start by adding the center ('i') which is not included in the hood localActivityDensity += activeDutyCycles_[i]; //include the center, which is 'i' (not included in hood) @@ -923,9 +922,6 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps // selected are treated as "bigger". vector alreadyUsedColumn(numColumns_, false); // in tie we prefer already used columns - const bool centerIncluded = std::find(neighborMap_.at(0).cbegin(), neighborMap_.at(0).cend(), 0) != neighborMap_.at(0).cend(); //TODO remove this if SP uses only hood w/o center - std::cout << "CENTER=" << centerIncluded << "\n"; - for (size_t column = 0; column < numColumns_; column++) { if (overlaps[column] < stimulusThreshold_) { //TODO make connections.computeActivity() already drop sub-threshold columns continue; @@ -937,6 +933,7 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps const auto& hood = neighborMap_.at(column); // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenion + const bool centerIncluded = std::find(neighborMap_.at(0).cbegin(), neighborMap_.at(0).cend(), 0) != neighborMap_.at(0).cend(); //TODO remove this if SP uses only hood w/o center const UInt numNeighbors = hood.size() + (centerIncluded ? -1 : 0); // +1 if "hood" does not includes the column itself (center); See #also2 NTA_ASSERT(numNeighbors >= 0); //const UInt numDesiredLocalActive = static_cast(ceil(density * (numNeighbors + 1))); From 93fb3a72d456bcc092d053670b33f23616e2febd Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 4 Jun 2020 12:49:02 +0200 Subject: [PATCH 29/43] TopologyTest: add case for wrap=true --- src/test/unit/utils/TopologyTest.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/test/unit/utils/TopologyTest.cpp b/src/test/unit/utils/TopologyTest.cpp index de90586afb..f90609a67b 100644 --- a/src/test/unit/utils/TopologyTest.cpp +++ b/src/test/unit/utils/TopologyTest.cpp @@ -376,12 +376,30 @@ const UInt center = 2; const UInt radius = 3; const vector dims = {10, 15}; -bool skipCenter = true; -for(const auto i: Neighborhood(center, radius, dims, /*wrap*/ false, /*skip center=*/ skipCenter )) { + +for(const auto i: Neighborhood(center, radius, dims, /*wrap*/ false, /*skip center=*/ true )) { + ASSERT_NE(center, i) << "Center should be skipped!"; +} + + +{ +bool centerFound_ = false; +for(const auto i: Neighborhood(center, radius, dims, /*wrap*/ false, /*skip center=*/ false )) { + if(i == center) { + centerFound_ = true; + break; + } +} +ASSERT_TRUE(centerFound_) << "Center was not included in hood!"; +} + + +for(const auto i: Neighborhood(center, radius, dims, /*wrap*/ true, /*skip center=*/ true )) { ASSERT_NE(center, i) << "Center should be skipped!"; } -skipCenter = false; +{ +bool skipCenter = false; bool centerFound_ = false; for(const auto i: Neighborhood(center, radius, dims, /*wrap*/ false, /*skip center=*/ skipCenter )) { if(i == center) { @@ -390,6 +408,8 @@ for(const auto i: Neighborhood(center, radius, dims, /*wrap*/ false, /*skip cent } } ASSERT_TRUE(centerFound_) << "Center was not included in hood!"; +} + } From 3a1dd5abeee7a8ee2686d077824aa9b3810e3255 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 4 Jun 2020 13:14:50 +0200 Subject: [PATCH 30/43] SP small change --- src/htm/algorithms/SpatialPooler.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 59d9ff5330..cbc8827a90 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -462,7 +462,6 @@ void SpatialPooler::initialize( } updateInhibitionRadius_(); - neighborMap_ = mapAllNeighbors(); if (spVerbosity_ > 0) { printParameters(); @@ -508,7 +507,7 @@ unordered_map> SpatialPooler::mapAllNeighbors() const { for(UInt column=0; column < numColumns_; column++) { vector neighbors; //of the current column - for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_, /*skip_center=*/false)) { + for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_, /*skip_center=*/true)) { neighbors.push_back(neighbor); } std::sort(neighbors.begin(), neighbors.end()); //sort for better cache locality @@ -597,8 +596,7 @@ vector SpatialPooler::initPermanence_(const vector &potential, //TOD void SpatialPooler::updateInhibitionRadius_() { if (globalInhibition_) { - inhibitionRadius_ = - *max_element(columnDimensions_.cbegin(), columnDimensions_.cend()); + setInhibitionRadius( *max_element(columnDimensions_.cbegin(), columnDimensions_.cend()) ); return; } From 4d5b2a1422335e558fa87bd87da0f85bb293fd8b Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 4 Jun 2020 13:33:45 +0200 Subject: [PATCH 31/43] SP rewrite mapAllMembers() as non-class method --- src/htm/algorithms/SpatialPooler.cpp | 28 ++++++++++++++++++---------- src/htm/algorithms/SpatialPooler.hpp | 2 -- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index cbc8827a90..9977322bee 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -164,7 +164,7 @@ void SpatialPooler::setInhibitionRadius(UInt inhibitionRadius) { NTA_ASSERT(inhibitionRadius > 0); if (inhibitionRadius_ != inhibitionRadius) { inhibitionRadius_ = inhibitionRadius; - neighborMap_ = mapAllNeighbors(); + neighborMap_ = mapAllNeighbors_(inhibitionRadius_, columnDimensions_, wrapAround_, /*skip_center=*/true); } } @@ -501,18 +501,26 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea } -unordered_map> SpatialPooler::mapAllNeighbors() const { //TODO move the cache logic to Neighbor class - std::unordered_map> neighborMap; - neighborMap.reserve(numColumns_); +unordered_map> mapAllNeighbors_(const Real radius, + const vector dimensions, + const bool wrapAround=true, + const bool skip_center=false) { //TODO move the cache logic to Neighbor class + std::unordered_map> neighborMap; + UInt numColumns = 1; + for(const auto dim: dimensions) { + numColumns*= dim; + } + neighborMap.reserve(numColumns); - for(UInt column=0; column < numColumns_; column++) { - vector neighbors; //of the current column - for(const auto neighbor: Neighborhood(column, inhibitionRadius_, columnDimensions_, wrapAround_, /*skip_center=*/true)) { - neighbors.push_back(neighbor); - } + for(UInt column=0; column < numColumns; column++) { + vector neighbors; //of the current column + for(const auto neighbor: Neighborhood(column, radius, dimensions, wrapAround, skip_center)) { + neighbors.push_back(neighbor); + } std::sort(neighbors.begin(), neighbors.end()); //sort for better cache locality neighbors.shrink_to_fit(); - neighborMap[column] = neighbors; + + neighborMap[column] = neighbors; } return neighborMap; } diff --git a/src/htm/algorithms/SpatialPooler.hpp b/src/htm/algorithms/SpatialPooler.hpp index 18b20ac670..dd5c576bd3 100644 --- a/src/htm/algorithms/SpatialPooler.hpp +++ b/src/htm/algorithms/SpatialPooler.hpp @@ -1141,8 +1141,6 @@ class SpatialPooler : public Serializable */ bool isUpdateRound_() const; - std::unordered_map> mapAllNeighbors() const; - //------------------------------------------------------------------- // Debugging helpers //------------------------------------------------------------------- From 191e406e5d22fffa5e650f1404b1c8876cb6baed Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 4 Jun 2020 14:35:25 +0200 Subject: [PATCH 32/43] SP move mapAllNeighbors to Topology it's a helper method for caching of Neighborhoods. Moved to Topology::Neighborhood::recalculateAllNeighborhoods() --- src/htm/algorithms/SpatialPooler.cpp | 27 +------------------------ src/htm/utils/Topology.cpp | 30 +++++++++++++++++++++++++++- src/htm/utils/Topology.hpp | 8 ++++++++ 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 9977322bee..e89481c984 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -164,7 +164,7 @@ void SpatialPooler::setInhibitionRadius(UInt inhibitionRadius) { NTA_ASSERT(inhibitionRadius > 0); if (inhibitionRadius_ != inhibitionRadius) { inhibitionRadius_ = inhibitionRadius; - neighborMap_ = mapAllNeighbors_(inhibitionRadius_, columnDimensions_, wrapAround_, /*skip_center=*/true); + neighborMap_ = Neighborhood::updateAllNeighbors(inhibitionRadius_, columnDimensions_, wrapAround_, /*skip_center=*/true); } } @@ -501,31 +501,6 @@ const vector SpatialPooler::compute(const SDR &input, const bool lea } -unordered_map> mapAllNeighbors_(const Real radius, - const vector dimensions, - const bool wrapAround=true, - const bool skip_center=false) { //TODO move the cache logic to Neighbor class - std::unordered_map> neighborMap; - UInt numColumns = 1; - for(const auto dim: dimensions) { - numColumns*= dim; - } - neighborMap.reserve(numColumns); - - for(UInt column=0; column < numColumns; column++) { - vector neighbors; //of the current column - for(const auto neighbor: Neighborhood(column, radius, dimensions, wrapAround, skip_center)) { - neighbors.push_back(neighbor); - } - std::sort(neighbors.begin(), neighbors.end()); //sort for better cache locality - neighbors.shrink_to_fit(); - - neighborMap[column] = neighbors; - } - return neighborMap; -} - - void SpatialPooler::boostOverlaps_(const vector &overlaps, //TODO use Eigen sparse vector here vector &boosted) const { if(boostStrength_ < htm::Epsilon) { //boost ~ 0.0, we can skip these computations, just copy the data diff --git a/src/htm/utils/Topology.cpp b/src/htm/utils/Topology.cpp index e255d352a3..1244eb9cec 100644 --- a/src/htm/utils/Topology.cpp +++ b/src/htm/utils/Topology.cpp @@ -23,8 +23,8 @@ #include #include // sort -using std::vector; using namespace htm; +using namespace std; namespace htm { @@ -231,5 +231,33 @@ void Neighborhood::Iterator::advance_() { } } +unordered_map> Neighborhood::updateAllNeighbors( + const Real radius, + const vector dimensions, + const bool wrapAround, + const bool skip_center) { //TODO move the cache logic to Neighbor class + + std::unordered_map> neighborMap; + size_t numColumns = 1; + for(const auto dim: dimensions) { + numColumns*= dim; + } + neighborMap.reserve(numColumns); + + + for(size_t column=0; column < numColumns; column++) { + vector neighbors; //of the current column + for(const auto neighbor: Neighborhood(column, radius, dimensions, wrapAround, skip_center)) { + neighbors.push_back(neighbor); + } + std::sort(neighbors.begin(), neighbors.end()); //sort for better cache locality + neighbors.shrink_to_fit(); + + neighborMap[column] = neighbors; + } + return neighborMap; +} + + Neighborhood::Iterator Neighborhood::begin() const { return {*this, false}; } Neighborhood::Iterator Neighborhood::end() const { return {*this, true}; } diff --git a/src/htm/utils/Topology.hpp b/src/htm/utils/Topology.hpp index d2406dd9e3..60076a4ed3 100644 --- a/src/htm/utils/Topology.hpp +++ b/src/htm/utils/Topology.hpp @@ -24,10 +24,12 @@ #include #include +#include #include #include #include +#include //just for CellIdx, maybe define separately? namespace htm { @@ -224,6 +226,12 @@ class Neighborhood { Iterator begin() const; Iterator end() const; +static std::unordered_map> updateAllNeighbors( + const Real radius, + const std::vector dimensions, + const bool wrapAround=true, + const bool skip_center=false); + private: const std::vector centerPosition_; const std::vector &dimensions_; From 5fe4eca3a40a5f30bac85d2d10892e9ff81ee624 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Thu, 3 Sep 2020 23:16:02 +0200 Subject: [PATCH 33/43] fix serialization for SP.neighborMap --- src/htm/algorithms/SpatialPooler.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.hpp b/src/htm/algorithms/SpatialPooler.hpp index c343d0d08c..8e8b72be21 100644 --- a/src/htm/algorithms/SpatialPooler.hpp +++ b/src/htm/algorithms/SpatialPooler.hpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace htm { @@ -37,8 +38,6 @@ namespace htm { using namespace std; /** - * CLA spatial pooler implementation in C++. - * * ### Description * The Spatial Pooler is responsible for creating a sparse distributed * representation of the input. Given an input it computes a set of sparse @@ -352,6 +351,9 @@ class SpatialPooler : public Serializable ar(CEREAL_NVP(rng_)); ar(CEREAL_NVP(minActiveDutyCycles_)); ar(CEREAL_NVP(boostedOverlaps_)); + + //re-initialize map + neighborMap_ = Neighborhood::updateAllNeighbors(inhibitionRadius_, columnDimensions_, wrapAround_, /*skip_center=*/true); } /** From 17168e695e8e1169a4a9b7832ffc84ac40b09478 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Sun, 6 Sep 2020 12:03:21 +0200 Subject: [PATCH 34/43] WIP debugging SP boost tests looks like float rounding err --- .../unit/algorithms/SpatialPoolerTest.cpp | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/test/unit/algorithms/SpatialPoolerTest.cpp b/src/test/unit/algorithms/SpatialPoolerTest.cpp index 3ddbf286cb..1230ac00b3 100644 --- a/src/test/unit/algorithms/SpatialPoolerTest.cpp +++ b/src/test/unit/algorithms/SpatialPoolerTest.cpp @@ -50,8 +50,7 @@ UInt countNonzero(const vector &vec) { } bool almost_eq(Real a, Real b) { - Real diff = a - b; - return (diff > -1e-5 && diff < 1e-5); + return (fabs(a - b) < 1e-5); } bool check_vector_eq(UInt arr[], vector vec) { //TODO replace with ArrayBase, VectorHelpers or teplates @@ -963,6 +962,7 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { SpatialPooler sp; setup(sp, 5, 6); + { //test1 Real32 initActiveDutyCycles1[] = {0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f}; Real32 initBoostFactors1[] = {0, 0, 0, 0, 0, 0}; vector trueBoostFactors1 = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; @@ -975,7 +975,9 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { sp.getBoostFactors(resultBoostFactors1.data()); ASSERT_EQ(resultBoostFactors1.size(), trueBoostFactors1.size()); ASSERT_TRUE(check_vector_eq(trueBoostFactors1, resultBoostFactors1)); + } + { Real32 initActiveDutyCycles2[] = {0.1f, 0.3f, 0.02f, 0.04f, 0.7f, 0.12f}; Real32 initBoostFactors2[] = {0, 0, 0, 0, 0, 0}; vector trueBoostFactors2 = {3.10599f, 0.42035f, 6.91251f, @@ -989,11 +991,13 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { sp.getBoostFactors(resultBoostFactors2.data()); ASSERT_EQ(resultBoostFactors2.size(), trueBoostFactors2.size()); ASSERT_TRUE(check_vector_eq(trueBoostFactors2, resultBoostFactors2)); + } + { Real32 initActiveDutyCycles3[] = {0.1f, 0.3f, 0.02f, 0.04f, 0.7f, 0.12f}; Real initBoostFactors3[] = {0, 0, 0, 0, 0, 0}; - vector trueBoostFactors3 = {1.25441f, 0.840857f, 1.47207f, - 1.41435f, 0.377822f, 1.20523f}; + const vector trueBoostFactors3 = {1.28586f, 0.795669f, 1.40094f, + 1.62534f, 0.367879f, 1.16682f}; vector resultBoostFactors3(6, 0); sp.setWrapAround(true); sp.setGlobalInhibition(false); @@ -1005,21 +1009,26 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { sp.getBoostFactors(resultBoostFactors3.data()); ASSERT_EQ(resultBoostFactors3.size(), trueBoostFactors3.size()); ASSERT_TRUE(check_vector_eq(trueBoostFactors3, resultBoostFactors3)); + } + { //test4: global inh + inh radius + boost str Real32 initActiveDutyCycles4[] = {0.1f, 0.3f, 0.02f, 0.04f, 0.7f, 0.12f}; Real32 initBoostFactors4[] = {0, 0, 0, 0, 0, 0}; - vector trueBoostFactors4 = {1.94773f, 0.263597f, 4.33476f, - 3.549f, 0.00482795f, 1.59467f}; + vector trueBoostFactors4 = {54.5981f, 7.38906f, 121.51f, + 99.4843f, 0.135335f, 44.7012f}; vector resultBoostFactors4(6, 0); sp.setGlobalInhibition(true); - sp.setBoostStrength(10); + sp.setBoostStrength(10.0); sp.setInhibitionRadius(3); sp.setBoostFactors(initBoostFactors4); sp.setActiveDutyCycles(initActiveDutyCycles4); sp.updateBoostFactors_(); sp.getBoostFactors(resultBoostFactors4.data()); - + for(UInt i=0; i< resultBoostFactors4.size(); i++) std::cout << "res " << resultBoostFactors4[i] << " vs " << trueBoostFactors4[i] << " is " + << (fabs(resultBoostFactors4[i] - trueBoostFactors4[i]) < 1e-5) << "\n"; //debug + ASSERT_EQ(resultBoostFactors4.size(), trueBoostFactors4.size()) << "size mismatch: " << resultBoostFactors4.size() << " vs. " << trueBoostFactors4.size(); ASSERT_TRUE(check_vector_eq(trueBoostFactors4, resultBoostFactors4)); + } } From 4e7d0809bd05f7d8cdd419768a2f93029852e9f5 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 08:56:18 +0200 Subject: [PATCH 35/43] fixed data for failing SP test, float rounding was the problem here. --- src/test/unit/algorithms/SpatialPoolerTest.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/unit/algorithms/SpatialPoolerTest.cpp b/src/test/unit/algorithms/SpatialPoolerTest.cpp index 1230ac00b3..4e00b45267 100644 --- a/src/test/unit/algorithms/SpatialPoolerTest.cpp +++ b/src/test/unit/algorithms/SpatialPoolerTest.cpp @@ -1014,8 +1014,8 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { { //test4: global inh + inh radius + boost str Real32 initActiveDutyCycles4[] = {0.1f, 0.3f, 0.02f, 0.04f, 0.7f, 0.12f}; Real32 initBoostFactors4[] = {0, 0, 0, 0, 0, 0}; - vector trueBoostFactors4 = {54.5981f, 7.38906f, 121.51f, - 99.4843f, 0.135335f, 44.7012f}; + vector trueBoostFactors4 = {54.598148, 7.38906, 121.51038, + 99.4843, 0.135335, 44.70118}; vector resultBoostFactors4(6, 0); sp.setGlobalInhibition(true); sp.setBoostStrength(10.0); @@ -1024,8 +1024,7 @@ TEST(SpatialPoolerTest, testUpdateBoostFactors) { sp.setActiveDutyCycles(initActiveDutyCycles4); sp.updateBoostFactors_(); sp.getBoostFactors(resultBoostFactors4.data()); - for(UInt i=0; i< resultBoostFactors4.size(); i++) std::cout << "res " << resultBoostFactors4[i] << " vs " << trueBoostFactors4[i] << " is " - << (fabs(resultBoostFactors4[i] - trueBoostFactors4[i]) < 1e-5) << "\n"; //debug + //for(UInt i=0; i< resultBoostFactors4.size(); i++) std::cout << std::fixed << std::setprecision(6) << "res " << resultBoostFactors4[i] << " vs " << trueBoostFactors4[i] << " is " << (fabs(resultBoostFactors4[i] - trueBoostFactors4[i]) < 1e-5) << "\n"; //debug ASSERT_EQ(resultBoostFactors4.size(), trueBoostFactors4.size()) << "size mismatch: " << resultBoostFactors4.size() << " vs. " << trueBoostFactors4.size(); ASSERT_TRUE(check_vector_eq(trueBoostFactors4, resultBoostFactors4)); } From fff728a293f0150f8fb7f2ac08cd8c5a442faff7 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 09:46:42 +0200 Subject: [PATCH 36/43] SP simplify neighborMap code to include only the "without center" hood variant. --- src/htm/algorithms/SpatialPooler.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index f95445d54c..53071f204b 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -621,7 +621,6 @@ void SpatialPooler::updateMinDutyCyclesLocal_() { for (UInt i = 0; i < numColumns_; i++) { Real maxOverlapDuty = overlapDutyCycles_[i]; //start with the center, which is column 'i' const auto& hood = neighborMap_[i]; - //for(const auto column : Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_, /*skip center=*/false)) { for(const auto column : hood) { maxOverlapDuty = max(maxOverlapDuty, overlapDutyCycles_[column]); } @@ -784,13 +783,9 @@ void SpatialPooler::updateBoostFactorsLocal_() { //optimization: In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenions. // Or in non-wrap, if we use cached hood, we obtain the value the same as hood.size() - const bool centerIncluded = std::find(hood.cbegin(), hood.cend(), i) != hood.cend(); //TODO avoid this once hood w/o center works for SP! - const UInt numNeighbors = hood.size() + (centerIncluded ? 0 : 1); - NTA_ASSERT(numNeighbors > 0); - if (! centerIncluded) { - //start by adding the center ('i') which is not included in the hood - localActivityDensity += activeDutyCycles_[i]; //include the center, which is 'i' (not included in hood) - } + const UInt numNeighbors = hood.size() + 1; + //start by adding the center ('i') which is not included in the hood + localActivityDensity += activeDutyCycles_[i]; //include the center, which is 'i' (not included in hood) //for(auto neighbor: Neighborhood(i, inhibitionRadius_, columnDimensions_, wrapAround_)) { for (const auto neighbor : hood) { @@ -912,18 +907,15 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps //..aka. how many times this column lost. const auto& hood = neighborMap_.at(column); - // In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, + // Optimization: In wrapAround, number of neighbors to be considered is solely a function of the inhibition radius, // the number of dimensions, and of the size of each of those dimenion - const bool centerIncluded = std::find(neighborMap_.at(0).cbegin(), neighborMap_.at(0).cend(), 0) != neighborMap_.at(0).cend(); //TODO remove this if SP uses only hood w/o center - const UInt numNeighbors = hood.size() + (centerIncluded ? -1 : 0); // +1 if "hood" does not includes the column itself (center); See #also2 - NTA_ASSERT(numNeighbors >= 0); + const UInt numNeighbors = hood.size(); //const UInt numDesiredLocalActive = static_cast(ceil(density * (numNeighbors + 1))); const UInt numDesiredLocalActive = static_cast(0.5f + (density * (numNeighbors + 1))); NTA_ASSERT(numDesiredLocalActive > 0); //for(auto neighbor: Neighborhood(column, inhibitionRadius_,columnDimensions_, wrapAround_, false /*skip center*/)) { for (const auto neighbor: hood) { - if(column == neighbor) continue; //TODO avoid this! NTA_ASSERT(neighbor != column); if (overlaps[neighbor] > overlaps[column] || ( (overlaps[neighbor] == overlaps[column]) && alreadyUsedColumn[neighbor])) { //this column lost to a neighbor @@ -937,6 +929,7 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps alreadyUsedColumn[column] = true; } } + NTA_ASSERT(activeColumns.size() == numDesiredLocalActive); //activeColumns.shrink_to_fit(); return activeColumns; } From 6fbe094b62a22cdeaca8af67260f0b09e9d039e1 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 10:10:00 +0200 Subject: [PATCH 37/43] fix assert --- src/htm/algorithms/SpatialPooler.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 53071f204b..7cf68756d8 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -929,7 +929,6 @@ vector SpatialPooler::inhibitColumnsLocal_(const vector &overlaps alreadyUsedColumn[column] = true; } } - NTA_ASSERT(activeColumns.size() == numDesiredLocalActive); //activeColumns.shrink_to_fit(); return activeColumns; } From a58acb3970dd1e5ceffb3d3b8b5f5fa90c5d1f19 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 10:33:11 +0200 Subject: [PATCH 38/43] Topology:updateAllNeighbors() fix radius type to UInt from Real, as spotted in Windows build. --- src/htm/utils/Topology.cpp | 2 +- src/htm/utils/Topology.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/htm/utils/Topology.cpp b/src/htm/utils/Topology.cpp index 1244eb9cec..8f8ecfd6e9 100644 --- a/src/htm/utils/Topology.cpp +++ b/src/htm/utils/Topology.cpp @@ -232,7 +232,7 @@ void Neighborhood::Iterator::advance_() { } unordered_map> Neighborhood::updateAllNeighbors( - const Real radius, + const UInt radius, const vector dimensions, const bool wrapAround, const bool skip_center) { //TODO move the cache logic to Neighbor class diff --git a/src/htm/utils/Topology.hpp b/src/htm/utils/Topology.hpp index 60076a4ed3..2c0d90b1dd 100644 --- a/src/htm/utils/Topology.hpp +++ b/src/htm/utils/Topology.hpp @@ -227,7 +227,7 @@ class Neighborhood { Iterator end() const; static std::unordered_map> updateAllNeighbors( - const Real radius, + const UInt radius, const std::vector dimensions, const bool wrapAround=true, const bool skip_center=false); From 628a4f9ac50179a29cb3bb9ba16ed45dc467051c Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 10:39:00 +0200 Subject: [PATCH 39/43] FIXME try disabling RESTapiTest.example segfault in Debug --- src/test/unit/engine/RESTapiTest.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/unit/engine/RESTapiTest.cpp b/src/test/unit/engine/RESTapiTest.cpp index 68c19e46df..e6b2b3fbd5 100644 --- a/src/test/unit/engine/RESTapiTest.cpp +++ b/src/test/unit/engine/RESTapiTest.cpp @@ -156,7 +156,8 @@ TEST_F(RESTapiTest, helloWorld) { EXPECT_STREQ(vm["result"].c_str(), "Hello World!") << "Response to GET /hi request"; } -TEST_F(RESTapiTest, example) { + +TEST_F(DISABLED_RESTapiTest, example) { //FIXME started segfaulting in CI Debug // A test similar to the Client Example. // Client thread. @@ -348,4 +349,4 @@ TEST_F(RESTapiTest, alternative_ids) { } -} // namespace testing \ No newline at end of file +} // namespace testing From 84c03b480db5d7e8c3dc3200b4501fd7c19327f2 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 12:18:15 +0200 Subject: [PATCH 40/43] another try disabling segfaulting RESTapiTest.example --- src/test/unit/algorithms/SpatialPoolerTest.cpp | 1 - src/test/unit/engine/RESTapiTest.cpp | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/unit/algorithms/SpatialPoolerTest.cpp b/src/test/unit/algorithms/SpatialPoolerTest.cpp index 4e00b45267..7a7ab8ceae 100644 --- a/src/test/unit/algorithms/SpatialPoolerTest.cpp +++ b/src/test/unit/algorithms/SpatialPoolerTest.cpp @@ -1290,7 +1290,6 @@ TEST(SpatialPoolerTest, testInhibitColumnsLocal) { sp.setInhibitionRadius(inhibitionRadius); active = sp.inhibitColumnsLocal_(overlaps, density); - for (auto a : active) std::cout << a << "\n"; ASSERT_EQ(active.size(), (size_t)4); ASSERT_TRUE(check_vector_eq(trueActive3, active)); } diff --git a/src/test/unit/engine/RESTapiTest.cpp b/src/test/unit/engine/RESTapiTest.cpp index e6b2b3fbd5..8003e197d4 100644 --- a/src/test/unit/engine/RESTapiTest.cpp +++ b/src/test/unit/engine/RESTapiTest.cpp @@ -156,8 +156,8 @@ TEST_F(RESTapiTest, helloWorld) { EXPECT_STREQ(vm["result"].c_str(), "Hello World!") << "Response to GET /hi request"; } - -TEST_F(DISABLED_RESTapiTest, example) { //FIXME started segfaulting in CI Debug +/* +TEST_F(RESTapiTest, example) { //FIXME started segfaulting in CI Debug // A test similar to the Client Example. // Client thread. @@ -247,6 +247,7 @@ TEST_F(DISABLED_RESTapiTest, example) { //FIXME started segfaulting in CI Debug } +*/ TEST_F(RESTapiTest, test_delete) { From 5beb5184bdc1bac5a3378b1571dea9c2b9ba77f7 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 12:57:31 +0200 Subject: [PATCH 41/43] type conversion on Windows --- src/htm/utils/Topology.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/htm/utils/Topology.cpp b/src/htm/utils/Topology.cpp index 8f8ecfd6e9..8770a8e79e 100644 --- a/src/htm/utils/Topology.cpp +++ b/src/htm/utils/Topology.cpp @@ -238,14 +238,14 @@ unordered_map> Neighborhood::updateAllNeighbors( const bool skip_center) { //TODO move the cache logic to Neighbor class std::unordered_map> neighborMap; - size_t numColumns = 1; + UInt numColumns = 1; for(const auto dim: dimensions) { numColumns*= dim; } neighborMap.reserve(numColumns); - for(size_t column=0; column < numColumns; column++) { + for(UInt column=0; column < numColumns; column++) { vector neighbors; //of the current column for(const auto neighbor: Neighborhood(column, radius, dimensions, wrapAround, skip_center)) { neighbors.push_back(neighbor); From f1c9ca2b1102dc82eec42414c05dcfb34f3e26f2 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 13:23:26 +0200 Subject: [PATCH 42/43] fix include for iota() on Windows --- src/htm/algorithms/SpatialPooler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 7cf68756d8..25ec19408c 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -23,6 +23,7 @@ #include #include //begin() #include //fmod +#include //iota #include #include From 4f594acbc3a0229f0c43426dcb88e5601fa2fbc7 Mon Sep 17 00:00:00 2001 From: Marek Otahal Date: Mon, 7 Sep 2020 16:01:58 +0200 Subject: [PATCH 43/43] run RESTapiTest.example only in Release as it crashes cpp-httplib in Debug. Should be unrelated to this PR, tracked in https://github.com/htm-community/htm.core/issues/884 --- src/test/unit/engine/RESTapiTest.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/unit/engine/RESTapiTest.cpp b/src/test/unit/engine/RESTapiTest.cpp index 8003e197d4..b19853a066 100644 --- a/src/test/unit/engine/RESTapiTest.cpp +++ b/src/test/unit/engine/RESTapiTest.cpp @@ -156,8 +156,8 @@ TEST_F(RESTapiTest, helloWorld) { EXPECT_STREQ(vm["result"].c_str(), "Hello World!") << "Response to GET /hi request"; } -/* -TEST_F(RESTapiTest, example) { //FIXME started segfaulting in CI Debug +#ifdef NDEBUG //FIXME cpp-httplib started segfaulting in Debug, see https://github.com/htm-community/htm.core/issues/884 +TEST_F(RESTapiTest, example) { // A test similar to the Client Example. // Client thread. @@ -247,7 +247,8 @@ TEST_F(RESTapiTest, example) { //FIXME started segfaulting in CI Debug } -*/ +#endif + TEST_F(RESTapiTest, test_delete) {