diff --git a/bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp b/bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp index b509bf6faf..41b20ffad6 100644 --- a/bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp +++ b/bindings/py/cpp_src/bindings/algorithms/py_SpatialPooler.cpp @@ -173,9 +173,9 @@ Argument boostStrength A number greater or equal than 0, used to too much boosting may also lead to instability of SP outputs. -Argument seed Seed for our random number generator. If seed is < 0 +Argument seed Seed for our random number generator. If seed is 0 a randomly generated seed is used. The behavior of the spatial - pooler is deterministic once the seed is set. + pooler is deterministic once the seed is set > 0. Argument spVerbosity spVerbosity level: 0, 1, 2, or 3 @@ -197,7 +197,7 @@ Argument wrapAround boolean value that determines whether or not inputs , py::arg("minPctOverlapDutyCycle") = 0.001 , py::arg("dutyCyclePeriod") = 1000 , py::arg("boostStrength") = 0.0 - , py::arg("seed") = 1 + , py::arg("seed") = 0 , py::arg("spVerbosity") = 0 , py::arg("wrapAround") = true ); diff --git a/bindings/py/cpp_src/bindings/algorithms/py_TemporalMemory.cpp b/bindings/py/cpp_src/bindings/algorithms/py_TemporalMemory.cpp index f202c0aa6c..f73904099d 100644 --- a/bindings/py/cpp_src/bindings/algorithms/py_TemporalMemory.cpp +++ b/bindings/py/cpp_src/bindings/algorithms/py_TemporalMemory.cpp @@ -112,7 +112,8 @@ Argument predictedSegmentDecrement 0.01 = 0.0004 Argument seed - Seed for the random number generator. + Seed for the random number generator. Default (0) means truly random, + > 0 is a fixed pseudorandom-sequence. See Random.hpp. Argument maxSegmentsPerCell The maximum number of segments per cell. @@ -146,7 +147,7 @@ Argument anomalyMode (optional, default ANMode::RAW) selects mode for `TM.anomal , py::arg("permanenceIncrement") = 0.1 , py::arg("permanenceDecrement") = 0.1 , py::arg("predictedSegmentDecrement") = 0.0 - , py::arg("seed") = 42 + , py::arg("seed") = 0 , py::arg("maxSegmentsPerCell") = 255 , py::arg("maxSynapsesPerSegment") = 255 , py::arg("checkInputs") = true diff --git a/src/examples/hello/HelloSPTP.cpp b/src/examples/hello/HelloSPTP.cpp index 962a89ebc1..c61a16bb84 100644 --- a/src/examples/hello/HelloSPTP.cpp +++ b/src/examples/hello/HelloSPTP.cpp @@ -95,6 +95,12 @@ EPOCHS = 2; // make test faster in Debug Metrics statsSPglobal(outSPglobal, 1000); Metrics statsTM(outTM, 1000); + //uses fixed seed for deterministic output checks: + Random rnd(42); + spGlobal.setSeed(1); + spLocal.setSeed(1); + tm.setSeed(42); + /* * For example: fn = sin(x) -> periodic >= 2Pi ~ 6.3 && x+=0.01 -> 630 steps to 1st period -> window >= 630 */ @@ -112,6 +118,7 @@ EPOCHS = 2; // make test faster in Debug for (UInt e = 0; e < EPOCHS; e++) { //FIXME EPOCHS is actually steps, there's just 1 pass through data/epoch. //Encode + { tEnc.start(); x+=0.01f; //step size for fn(x) enc.encode(sin(x), input); //model sin(x) function //TODO replace with CSV data @@ -121,32 +128,38 @@ EPOCHS = 2; // make test faster in Debug tRng.start(); input.addNoise(0.01f, rnd); //change 1% of the SDR for each iteration, this makes a random sequence, but seemingly stable tRng.stop(); + } //SP (global and local) if(useSPlocal) { - tSPloc.start(); - spLocal.compute(input, true, outSPlocal); - tSPloc.stop(); + tSPloc.start(); + spLocal.compute(input, true, outSPlocal); + tSPloc.stop(); + + outSP = outSPlocal; } if(useSPglobal) { - tSPglob.start(); - spGlobal.compute(input, true, outSPglobal); - tSPglob.stop(); + tSPglob.start(); + spGlobal.compute(input, true, outSPglobal); + tSPglob.stop(); + + outSP = outSPglobal; } - outSP = outSPglobal; //toggle if local/global SP is used further down the chain (TM, Anomaly) // TM if(useTM) { - tTM.start(); + tTM.start(); tm.compute(outSP, true /*learn*/); //to uses output of SPglobal tm.activateDendrites(); //required to enable tm.getPredictiveCells() - outTM = tm.cellsToColumns( tm.getPredictiveCells() ); tTM.stop(); + + outTM = tm.cellsToColumns( tm.getPredictiveCells() ); } //Anomaly (pure x likelihood) + { an = tm.anomaly; avgAnom10.compute(an); //moving average if(e % 1000 == 0) { @@ -157,6 +170,7 @@ EPOCHS = 2; // make test faster in Debug tAnLikelihood.start(); anLikely = anLikelihood.anomalyProbability(an); tAnLikelihood.stop(); + } // print @@ -202,27 +216,36 @@ EPOCHS = 2; // make test faster in Debug SDR goldSP({COLS}); const SDR_sparse_t deterministicSP{ - 62, 72, 73, 82, 85, 102, 263, 277, 287, 303, 306, 308, 309, 322, 337, 339, 340, 352, 370, 493, 1094, 1095, 1114, 1115, 1120, 1463, 1512, 1518, 1647, 1651, 1691, 1694, 1729, 1745, 1746, 1760, 1770, 1774, 1775, 1781, 1797, 1798, 1803, 1804, 1805, 1812, 1827, 1828, 1831, 1832, 1858, 1859, 1860, 1861, 1862, 1875, 1878, 1880, 1881, 1898, 1918, 1923, 1929, 1931,1936, 1950, 1953, 1956, 1958, 1961, 1964, 1965, 1967, 1971, 1973, 1975, 1976, 1979, 1980, 1981, 1982, 1984, 1985, 1986, 1988, 1991, 1994, 1996, 1997, 1998, 1999, 2002, 2006, 2008, 2011, 2012, 2013, 2017, 2019, 2022, 2027, 2030 + 68, 79, 86, 98, 105, 257, 263, 286, 302, 306, 307, 309, 310, 313, 315, 317, 318, 320, 323, 325, 326, 329, 334, 350, 356, 363, 539, 935, 1089, 1093, 1098, 1111, 1112, 1118, 1120, 1124, 1133, 1508, 1513, 1521, 1624, 1746, 1765, 1774, 1775, 1776, 1780, 1784, 1787, 1802, 1804, 1811, 1813, 1815, 1819, 1844, 1845, 1865, 1876, 1884, 1891, 1900, 1903, 1904, 1908, 1909, 1925, 1926, 1928, 1932, 1933, 1943, 1947, 1952, 1955, 1959, 1961, 1962, 1964, 1966, 1967, 1969, 1970, 1971, 1973, 1975, 1977, 1980, 1981, 1982, 1983, 1985, 1987, 1991, 1994, 2002, 2004, 2011, 2027, 2030, 2031, 2045 }; goldSP.setSparse(deterministicSP); SDR goldSPlocal({COLS}); const SDR_sparse_t deterministicSPlocal{ - 12, 13, 71, 72, 75, 78, 82, 85, 131, 171, 182, 186, 189, 194, 201, 263, 277, 287, 308, 319, 323, 337, 339, 365, 407, 429, 432, 434, 443, 445, 493, 494, 502, 508, 523, 542, 554, 559, 585, 586, 610, 611, 612, 644, 645, 647, 691, 698, 699, 701, 702, 707, 777, 809, 810, 811, 833, 839, 841, 920, 923, 928, 929, 935, 955, 1003, 1005, 1073, 1076, 1094, 1095, 1114, 1115, 1133, 1134, 1184, 1203, 1232, 1233, 1244, 1253, 1268, 1278, 1291, 1294, 1306, 1309, 1331, 1402, 1410, 1427, 1434, 1442, 1463, 1508, 1512, 1514, 1515, 1518, 1561, 1564, 1623, 1626, 1630, 1640, 1647, 1691, 1694, 1729, 1745, 1746, 1760, 1797, 1804, 1805, 1812, 1827, 1831, 1858, 1861, 1862, 1918, 1956, 1961, 1965, 1971, 1975, 1994, 2012 + 17, 71, 75, 79, 81, 86, 89, 164, 189, 198, 203, 262, 297, 314, 324, 326, 329, 337, 360, 379, 432, 443, 448, 452, 509, 520, 525, 526, 529, 536, 612, 619, 624, 630, 649, 652, 693, 717, 719, 720, 754, 810, 813, 815, 835, 839, 849, 884, 890, 914, 925, 931, 937, 945, 971, 1016, 1088, 1089, 1095, 1105, 1109, 1133, 1159, 1209, 1214, 1228, 1235, 1241, 1244, 1273, 1295, 1314, 1329, 1336, 1342, 1427, 1435, 1436, 1448, 1461, 1486, 1496, 1500, 1523, 1561, 1572, 1576, 1603, 1610, 1624, 1635, 1649, 1664, 1685, 1725, 1732, 1741, 1758, 1800, 1804, 1811, 1824, 1862, 1870, 1882, 1883, 1887, 1903, 1956, 1963, 1971, 1977, 1984, 2015 }; goldSPlocal.setSparse(deterministicSPlocal); SDR goldTM({COLS}); const SDR_sparse_t deterministicTM{ - 72, 85, 102, 114, 122, 126, 287, 308, 337, 339, 542, 920, 939, 952, 1268, 1507, 1508, 1518, 1546, 1547, 1626, 1627, 1633, 1668, 1727, 1804, 1805, 1827, 1832, 1844, 1859, 1862, 1918, 1920, 1924, 1931, 1933, 1945, 1961, 1965, 1966, 1968, 1970, 1973, 1975, 1976, 1977, 1979, 1986, 1987, 1991, 1992, 1996, 1998, 2002, 2006, 2008, 2012, 2042, 2045 + 79, 92, 98, 128, 136, 286, 307, 309, 310, 313, 315, 325, 356, 454, 539, 1093, 1111, 1112, 1120, 1237, 1278, 1467, 1497, 1508, 1513, 1521, 1614, 1624, 1635, 1668, 1669, 1673,1699, 1774, 1775, 1776, 1780, 1784, 1808, 1813, 1815, 1816, 1821, 1827, 1845, 1865, 1900, 1911, 1913, 1925, 1929, 1931, 1932, 1933, 1940, 1941, 1947, 1949, 1955, 1956, 1959, 1961, 1962, 1964, 1966, 1967, 1968, 1969, 1970, 1972, 1975, 1977, 1978, 1979, 1981, 1982, 1985, 1987, 1988, 1991, 1994, 2027, 2030, 2044, 2045 }; goldTM.setSparse(deterministicTM); - const float goldAn = 0.637255f; //Note: this value is for a (randomly picked) datapoint, it does not have to improve (decrease) with better algorithms - const float goldAnAvg = 0.40804f; // ...the averaged value, on the other hand, should improve/decrease. + const float goldAn = 0.470588f; //Note: this value is for a (randomly picked) datapoint, it does not have to improve (decrease) with better algorithms + const float goldAnAvg = 0.40961f; // ...the averaged value, on the other hand, should improve/decrease. #ifdef _ARCH_DETERMINISTIC if(e+1 == 5000) { + // For debugging serialization: save SP's state in 1 step, comment out, recompile, load SP and compare in another + // step 1: + spGlobal.saveToFile("/tmp/spG.save"); + // step 2: + SpatialPooler resumedSP; + resumedSP.loadFromFile("/tmp/spG.save"); + NTA_CHECK(spGlobal == resumedSP) << "SPs differ!"; + // --end of debugging + //these hand-written values are only valid for EPOCHS = 5000 (default), but not for debug and custom runs. NTA_CHECK(input == goldEnc) << "Deterministic output of Encoder failed!\n" << input << "should be:\n" << goldEnc; if(useSPglobal) { NTA_CHECK(outSPglobal == goldSP) << "Deterministic output of SP (g) failed!\n" << outSP << "should be:\n" << goldSP; } diff --git a/src/htm/algorithms/Connections.cpp b/src/htm/algorithms/Connections.cpp index b6361fd35f..cc5ef2597f 100644 --- a/src/htm/algorithms/Connections.cpp +++ b/src/htm/algorithms/Connections.cpp @@ -31,6 +31,8 @@ using std::string; using std::vector; using namespace htm; +const Permanence MARK_SYNAPSE_AS_REMOVED = -1.0; + Connections::Connections(const CellIdx numCells, const Permanence connectedThreshold, const bool timeseries) { @@ -210,14 +212,14 @@ bool Connections::synapseExists_(const Synapse synapse, bool fast) const { const bool found = (std::find(synapsesOnSegment.begin(), synapsesOnSegment.end(), synapse) != synapsesOnSegment.end()); //validate the fast & slow methods for same result: #ifdef NTA_ASSERTIONS_ON - const bool removed = synapses_[synapse].permanence == -1; - NTA_ASSERT( (removed and not found) or (not removed and found) ); + const bool removed = fabs(synapses_[synapse].permanence - MARK_SYNAPSE_AS_REMOVED) < 0.001f; + NTA_CHECK( (removed and not found) or (not removed and found) ); #endif return found; } else { //quick method. Relies on hack in destroySynapse() where we set synapseData.permanence == -1 - return synapses_[synapse].permanence != -1; + return fabs(synapses_[synapse].permanence - MARK_SYNAPSE_AS_REMOVED) < 0.001f; } } @@ -261,8 +263,8 @@ void Connections::destroySegment(const Segment segment) { CellData &cellData = cells_[segmentData.cell]; const auto segmentOnCell = std::find(cellData.segments.cbegin(), cellData.segments.cend(), segment); - NTA_ASSERT(segmentOnCell != cellData.segments.cend()) << "Segment to be destroyed not found on the cell!"; - NTA_ASSERT(*segmentOnCell == segment); + NTA_CHECK(segmentOnCell != cellData.segments.cend()) << "Segment to be destroyed not found on the cell!"; + NTA_CHECK(*segmentOnCell == segment); cellData.segments.erase(segmentOnCell); destroyedSegments_++; @@ -313,15 +315,18 @@ void Connections::destroySynapse(const Synapse synapse) { [&](const Synapse a, const Synapse b) -> bool { return dataForSynapse(a).id < dataForSynapse(b).id;} ); - NTA_ASSERT(synapseOnSegment != segmentData.synapses.cend()); - NTA_ASSERT(*synapseOnSegment == synapse); + NTA_CHECK(synapseOnSegment != segmentData.synapses.cend()); + NTA_CHECK(*synapseOnSegment == synapse); segmentData.synapses.erase(synapseOnSegment); //Note: dataForSynapse(synapse) are not deleted, unfortunately. And are still accessible. //To mark them as "removed", we set SynapseData.permanence = -1, this can be used for a quick check later - synapseData.permanence = -1; //marking as "removed" + synapseData.permanence = MARK_SYNAPSE_AS_REMOVED; //marking as "removed" + //actually erase from synapses_ : + //const auto it = synapses_.begin() + synapse; + //synapses_.erase(it); //slow destroyedSynapses_++; - NTA_ASSERT(not synapseExists_(synapse)); + NTA_ASSERT(not synapseExists_(synapse, false)); } @@ -768,6 +773,7 @@ bool Connections::operator==(const Connections &o) const { NTA_CHECK (cells_.size() == o.cells_.size()) << "Connections equals: cells_" << cells_.size() << " vs. " << o.cells_.size(); NTA_CHECK (cells_ == o.cells_) << "Connections equals: cells_" << cells_.size() << " vs. " << o.cells_.size(); + NTA_CHECK (segments_.size() == o.segments_.size()) << "Connections equals: segments_ size: " << segments_.size() << " vs. " << o.segments_.size(); NTA_CHECK (segments_ == o.segments_ ) << "Connections equals: segments_"; NTA_CHECK (destroyedSegments_ == o.destroyedSegments_ ) << "Connections equals: destroyedSegments_"; @@ -777,6 +783,7 @@ bool Connections::operator==(const Connections &o) const { //also check underlying datastructures (segments, and subsequently synapses). Can be time consuming. //1.cells: + /* for(const auto cellD : cells_) { //2.segments: const auto& segments = cellD.segments; @@ -789,6 +796,7 @@ bool Connections::operator==(const Connections &o) const { } } } + */ NTA_CHECK (connectedThreshold_ == o.connectedThreshold_ ) << "Connections equals: connectedThreshold_"; @@ -810,7 +818,10 @@ bool Connections::operator==(const Connections &o) const { NTA_CHECK (prunedSegs_ == o.prunedSegs_ ) << "Connections equals: prunedSegs_"; } catch(const htm::Exception& ex) { - std::cout << "Connection equals: differ! " << ex.what(); + UNUSED(ex); //avoid warning if the debug below is not used. + //uncomment below to start debugging Connections op==. It's perfectly + //..normal for unequality here. + NTA_WARN << "Connection equals: differ! " << ex.what(); return false; } return true; diff --git a/src/htm/algorithms/Connections.hpp b/src/htm/algorithms/Connections.hpp index 0407e1efef..9bde2813bb 100644 --- a/src/htm/algorithms/Connections.hpp +++ b/src/htm/algorithms/Connections.hpp @@ -95,7 +95,7 @@ struct SynapseData: public Serializable { NTA_CHECK(id == o.id ) << "SynapseData equals: id"; } catch(const htm::Exception& ex) { UNUSED(ex); // this avoids the warning if ex is not used. - //NTA_WARN << "SynapseData equals: " << ex.what(); //Note: uncomment for debug, tells you + NTA_WARN << "SynapseData equals: " << ex.what(); //Note: uncomment for debug, tells you //where the diff is. It's perfectly OK for the "exception" to occur, as it just denotes //that the data is NOT equal. return false; @@ -155,7 +155,7 @@ struct SegmentData: public Serializable { } catch(const htm::Exception& ex) { UNUSED(ex); // this avoids the warning if ex is not used. - //NTA_WARN << "SegmentData equals: " << ex.what(); + NTA_WARN << "SegmentData equals: " << ex.what(); return false; } return true; @@ -195,7 +195,7 @@ struct CellData : public Serializable { NTA_CHECK( segments == o.segments ) << "CellData equals: segments"; } catch(const htm::Exception& ex) { UNUSED(ex); // this avoids the warning if ex is not used. - //NTA_WARN << "CellData equals: " << ex.what(); + NTA_WARN << "CellData equals: " << ex.what(); return false; } return true; @@ -464,7 +464,7 @@ class Connections : public Serializable * @retval Synapse data. */ inline const SynapseData& dataForSynapse(const Synapse synapse) const { - NTA_CHECK(synapseExists_(synapse, true)); + NTA_CHECK(synapseExists_(synapse, false)); return synapses_[synapse]; } diff --git a/src/htm/algorithms/SpatialPooler.cpp b/src/htm/algorithms/SpatialPooler.cpp index 25ec19408c..0f708d244a 100644 --- a/src/htm/algorithms/SpatialPooler.cpp +++ b/src/htm/algorithms/SpatialPooler.cpp @@ -72,17 +72,24 @@ SpatialPooler::SpatialPooler() { } SpatialPooler::SpatialPooler( - const vector inputDimensions, const vector columnDimensions, - UInt potentialRadius, Real potentialPct, bool globalInhibition, - Real localAreaDensity, UInt numActiveColumnsPerInhArea, - UInt stimulusThreshold, Real synPermInactiveDec, Real synPermActiveInc, - Real synPermConnected, Real minPctOverlapDutyCycles, UInt dutyCyclePeriod, - Real boostStrength, Int seed, UInt spVerbosity, bool wrapAround) - : SpatialPooler::SpatialPooler() + const vector inputDimensions, + const vector columnDimensions, + UInt potentialRadius, + Real potentialPct, + bool globalInhibition, + Real localAreaDensity, + UInt numActiveColumnsPerInhArea, + UInt stimulusThreshold, + Real synPermInactiveDec, + Real synPermActiveInc, + Real synPermConnected, + Real minPctOverlapDutyCycles, + UInt dutyCyclePeriod, + Real boostStrength, + UInt seed, + UInt spVerbosity, + bool wrapAround) : SpatialPooler::SpatialPooler() { - // The current version number for serialzation. - version_ = 2; - initialize(inputDimensions, columnDimensions, potentialRadius, @@ -117,14 +124,14 @@ UInt SpatialPooler::getNumInputs() const { return numInputs_; } UInt SpatialPooler::getPotentialRadius() const { return potentialRadius_; } void SpatialPooler::setPotentialRadius(UInt potentialRadius) { - NTA_CHECK(potentialRadius < numInputs_); + NTA_CHECK(potentialRadius < numInputs_) << "SP setPotentialRadius: " << potentialRadius << " must be < " << numInputs_; potentialRadius_ = potentialRadius; } Real SpatialPooler::getPotentialPct() const { return potentialPct_; } void SpatialPooler::setPotentialPct(Real potentialPct) { - NTA_CHECK(potentialPct > 0.0f && potentialPct <= 1.0f); + NTA_CHECK(potentialPct > 0.0f && potentialPct <= 1.0f) << "SP setPotentialPct(): out of bounds (0, 1]"; potentialPct_ = potentialPct; } @@ -386,7 +393,7 @@ void SpatialPooler::initialize( Real minPctOverlapDutyCycles, UInt dutyCyclePeriod, Real boostStrength, - Int seed, + UInt seed, UInt spVerbosity, bool wrapAround) { @@ -420,9 +427,9 @@ void SpatialPooler::initialize( rng_ = Random(seed); - potentialRadius_ = potentialRadius > numInputs_ ? numInputs_ : potentialRadius; - NTA_CHECK(potentialPct > 0 && potentialPct <= 1); - potentialPct_ = potentialPct; + potentialRadius_ = min(numInputs_ , potentialRadius); + //!setPotentialRadius(potentialRadius); + setPotentialPct(potentialPct);; globalInhibition_ = globalInhibition; stimulusThreshold_ = stimulusThreshold; synPermInactiveDec_ = synPermInactiveDec; @@ -1001,45 +1008,91 @@ void SpatialPooler::printState(const vector &state, std::ostream& out) con } -/** equals implementation based on text serialization */ bool SpatialPooler::operator==(const SpatialPooler& o) const{ - // Store the simple variables first. - if (numInputs_ != o.numInputs_) return false; - if (numColumns_ != o.numColumns_) return false; - if (potentialRadius_ != o.potentialRadius_) return false; - if (potentialPct_ != o.potentialPct_) return false; - if (initConnectedPct_ != o.initConnectedPct_) return false; - if (globalInhibition_ != o.globalInhibition_) return false; - if (numActiveColumnsPerInhArea_ != o.numActiveColumnsPerInhArea_) return false; - if (localAreaDensity_ != o.localAreaDensity_) return false; - if (stimulusThreshold_ != o.stimulusThreshold_) return false; - if (inhibitionRadius_ != o.inhibitionRadius_) return false; - if (dutyCyclePeriod_ != o.dutyCyclePeriod_) return false; - if (boostStrength_ != o.boostStrength_) return false; - if (iterationNum_ != o.iterationNum_) return false; - if (iterationLearnNum_ != o.iterationLearnNum_) return false; - if (spVerbosity_ != o.spVerbosity_) return false; - if (updatePeriod_ != o.updatePeriod_) return false; - if (synPermInactiveDec_ != o.synPermInactiveDec_) return false; - if (synPermActiveInc_ != o.synPermActiveInc_) return false; - if (synPermBelowStimulusInc_ != o.synPermBelowStimulusInc_) return false; - if (synPermConnected_ != o.synPermConnected_) return false; - if (minPctOverlapDutyCycles_ != o.minPctOverlapDutyCycles_) return false; - if (wrapAround_ != o.wrapAround_) return false; + //Note on implementation: NTA_CHECKs are used, so we know + //what condition failed. + try { + + // Check SP's derect member variables first:. + NTA_CHECK (numInputs_ == o.numInputs_) << "SP equals: numInputs:" << numInputs_ << " vs. " << o.numInputs_ ; + NTA_CHECK (numColumns_ == o.numColumns_) << "SP equals: numColumns: " << numColumns_ << " vs. " << o.numColumns_; + NTA_CHECK (potentialRadius_ == o.potentialRadius_) << "SP equals: potentialRadius: " << potentialRadius_ << " vs. " << o.potentialRadius_; + NTA_CHECK (potentialPct_ == o.potentialPct_) << "SP equals: potentialPct: " << potentialPct_ << " vs. " << o.potentialPct_; + NTA_CHECK (initConnectedPct_ == o.initConnectedPct_) << "SP equals: initConnectedPct: " << initConnectedPct_ << " vs. " << o.initConnectedPct_; + NTA_CHECK (globalInhibition_ == o.globalInhibition_) << "SP equals: globalInhibition: " << globalInhibition_ << " vs. " << o.globalInhibition_; + NTA_CHECK (numActiveColumnsPerInhArea_ == o.numActiveColumnsPerInhArea_) << "SP equals: numActiveColumnsPerInhArea: " + << numActiveColumnsPerInhArea_ << " vs. " << o.numActiveColumnsPerInhArea_; + NTA_CHECK (localAreaDensity_ == o.localAreaDensity_) << "SP equals: localAreaDensity: " << localAreaDensity_ << " vs. " << o.localAreaDensity_; + NTA_CHECK (stimulusThreshold_ == o.stimulusThreshold_) << "SP equals: stimulusThreshold: " << stimulusThreshold_ << " vs. " << o.stimulusThreshold_; + NTA_CHECK (inhibitionRadius_ == o.inhibitionRadius_) << "SP equals: inhibitionRadius: " << inhibitionRadius_ << " vs. " << o.inhibitionRadius_; + NTA_CHECK (dutyCyclePeriod_ == o.dutyCyclePeriod_) << "SP equals: dutyCyclePeriod: " << dutyCyclePeriod_ << " vs. " << o.dutyCyclePeriod_; + NTA_CHECK (boostStrength_ == o.boostStrength_) << "SP equals: boostStrength: " << boostStrength_ << " vs. " << o.boostStrength_; + NTA_CHECK (iterationNum_ == o.iterationNum_) << "SP equals: iterationNum: " << iterationNum_ << " vs. " << o.iterationNum_; + NTA_CHECK (iterationLearnNum_ == o.iterationLearnNum_) << "SP equals: iterationLearnNum: " << iterationLearnNum_ << " vs. " << o.iterationLearnNum_; + NTA_CHECK (spVerbosity_ == o.spVerbosity_) << "SP equals: spVerbosity: " << spVerbosity_ << " vs. " << o.spVerbosity_; + NTA_CHECK (updatePeriod_ == o.updatePeriod_) << "SP equals: updatePeriod: " << updatePeriod_ << " vs. " << o.updatePeriod_; + NTA_CHECK (synPermInactiveDec_ == o.synPermInactiveDec_) << "SP equals: synPermInactiveDec: " << synPermInactiveDec_ << " vs. " << o.synPermInactiveDec_; + NTA_CHECK (synPermActiveInc_ == o.synPermActiveInc_) << "SP equals: synPermActiveInc: " << synPermActiveInc_ << " vs. " << o.synPermActiveInc_; + NTA_CHECK (synPermBelowStimulusInc_ == o.synPermBelowStimulusInc_) << "SP equals: synPermBelowStimulusInc: " << synPermBelowStimulusInc_ << " vs. " << o.synPermBelowStimulusInc_; + NTA_CHECK (synPermConnected_ == o.synPermConnected_) << "SP equals: synPermConnected: " << synPermConnected_ << " vs. " << o.synPermConnected_; + NTA_CHECK (minPctOverlapDutyCycles_ == o.minPctOverlapDutyCycles_) + << "SP equals: minPctOverlapDutyCycles: " << minPctOverlapDutyCycles_ << " vs. " << minPctOverlapDutyCycles_; + NTA_CHECK (wrapAround_ == o.wrapAround_) << "SP equals: wrapAround: " << wrapAround_ << " vs. " << o.wrapAround_; + + //Random + NTA_CHECK (rng_ == o.rng_) << "SP equals: rng differs"; + + // compare connections + NTA_CHECK (connections_ == o.connections_) << "SP equals: connections: " << connections_ << " vs. " << o.connections_; + // compare vectors. - if (inputDimensions_ != o.inputDimensions_) return false; - if (columnDimensions_ != o.columnDimensions_) return false; - if (boostFactors_ != o.boostFactors_) return false; - if (overlapDutyCycles_ != o.overlapDutyCycles_) return false; - if (activeDutyCycles_ != o.activeDutyCycles_) return false; - if (minOverlapDutyCycles_ != o.minOverlapDutyCycles_) return false; + NTA_CHECK (inputDimensions_ == o.inputDimensions_) << "SP equals: inputDimensions differ"; + NTA_CHECK (columnDimensions_ == o.columnDimensions_) << "SP equals: columnDimensions differ"; + NTA_CHECK (boostFactors_ == o.boostFactors_) << "SP equals: boostFactors"; + NTA_CHECK (overlapDutyCycles_ == o.overlapDutyCycles_) << "SP equals: overlapDutyCycles"; //FIXME seems bug is here?! + NTA_CHECK (activeDutyCycles_ == o.activeDutyCycles_) << "SP equals: activeDutyCucles"; //FIXME here? + NTA_CHECK (minOverlapDutyCycles_ == o.minOverlapDutyCycles_) << "SP equals: minOverlapDutyCycles"; + + //detailed compare potentials + for (UInt i = 0; i < numColumns_; i++) { + std::vector potential1(numInputs_, 0); + std::vector potential2(numInputs_, 0); + this->getPotential(i, potential1.data()); //TODO make the method return vect? + o.getPotential(i, potential2.data()); + NTA_CHECK(potential1 == potential2) << "SP equals: potentials"; //FIXME + } + + // check get permanences + for (UInt i = 0; i < numColumns_; i++) { + const auto& perm1 = this->getPermanence(i); + const auto& perm2 = o.getPermanence(i); + NTA_CHECK(perm1 == perm2) << "SP equals: permanences"; + } + + // check get connected synapses + for (UInt i = 0; i < numColumns_; i++) { + const auto& con1 = this->getPermanence(i, this->connections.getConnectedThreshold()); + const auto& con2 = o.getPermanence(i, o.connections.getConnectedThreshold()); + NTA_CHECK(con1 == con2) << "SP equals: connected synapses"; + } + + { + std::vector conCounts1(numColumns_, 0); + std::vector conCounts2(numColumns_, 0); + this->getConnectedCounts(conCounts1.data()); + o.getConnectedCounts(conCounts2.data()); + NTA_CHECK(conCounts1 == conCounts2) << "SP equals: connected column counts"; + } // compare connections - if (connections_ != o.connections_) return false; + NTA_CHECK (connections_ == o.connections_) << "SP equals: connections: " << connections_ << " vs. " << o.connections_; - //Random - if (rng_ != o.rng_) return false; + } catch(const htm::Exception& ex) { + //some check failed -> not equal + NTA_WARN << "SP unequal output: " << ex.what(); + return false; + } return true; } diff --git a/src/htm/algorithms/SpatialPooler.hpp b/src/htm/algorithms/SpatialPooler.hpp index 8e8b72be21..d9b02555e1 100644 --- a/src/htm/algorithms/SpatialPooler.hpp +++ b/src/htm/algorithms/SpatialPooler.hpp @@ -75,7 +75,7 @@ class SpatialPooler : public Serializable Real minPctOverlapDutyCycles = 0.001f, UInt dutyCyclePeriod = 1000u, Real boostStrength = 0.0f, - Int seed = 1, + UInt seed = 0u, //random UInt spVerbosity = 0u, bool wrapAround = true); @@ -200,9 +200,9 @@ class SpatialPooler : public Serializable too much boosting may also lead to instability of SP outputs. - @param seed Seed for our random number generator. If seed is < 0 + @param seed Seed for our random number generator. If seed is 0, a randomly generated seed is used. The behavior of the spatial - pooler is deterministic once the seed is set. + pooler is deterministic once the seed is fixed to > 0. @param spVerbosity spVerbosity level: 0, 1, 2, or 3 @@ -220,10 +220,15 @@ class SpatialPooler : public Serializable Real localAreaDensity = 0.05f, UInt numActiveColumnsPerInhArea = 0, UInt stimulusThreshold = 0u, - Real synPermInactiveDec = 0.01f, Real synPermActiveInc = 0.1f, - Real synPermConnected = 0.1f, Real minPctOverlapDutyCycles = 0.001f, - UInt dutyCyclePeriod = 1000u, Real boostStrength = 0.0f, - Int seed = 1, UInt spVerbosity = 0u, bool wrapAround = true); + Real synPermInactiveDec = 0.01f, + Real synPermActiveInc = 0.1f, + Real synPermConnected = 0.1f, + Real minPctOverlapDutyCycles = 0.001f, + UInt dutyCyclePeriod = 1000u, + Real boostStrength = 0.0f, + UInt seed = 0u, + UInt spVerbosity = 0u, + bool wrapAround = true); /** @@ -779,6 +784,17 @@ class SpatialPooler : public Serializable */ void getConnectedCounts(UInt connectedCounts[]) const; + /** set seed for internal random number generator. + * See @ref `seed` arg in the constructor. + */ + void setSeed(const UInt seed) { + rng_ = Random(seed); + NTA_CHECK(seed == getSeed()) << "SP: Seed not set correctly"; + } + UInt getSeed() const { + return rng_.getSeed(); + } + /** Returns the boosted overlap score for each column. diff --git a/src/htm/algorithms/TemporalMemory.cpp b/src/htm/algorithms/TemporalMemory.cpp index d9287e4b98..d5dabb643a 100644 --- a/src/htm/algorithms/TemporalMemory.cpp +++ b/src/htm/algorithms/TemporalMemory.cpp @@ -63,7 +63,7 @@ TemporalMemory::TemporalMemory( Permanence permanenceIncrement, Permanence permanenceDecrement, Permanence predictedSegmentDecrement, - Int seed, + UInt seed, SegmentIdx maxSegmentsPerCell, SynapseIdx maxSynapsesPerSegment, bool checkInputs, @@ -90,7 +90,7 @@ void TemporalMemory::initialize( Permanence permanenceIncrement, Permanence permanenceDecrement, Permanence predictedSegmentDecrement, - Int seed, + UInt seed, SegmentIdx maxSegmentsPerCell, SynapseIdx maxSynapsesPerSegment, bool checkInputs, diff --git a/src/htm/algorithms/TemporalMemory.hpp b/src/htm/algorithms/TemporalMemory.hpp index 56c8825114..18fdbb7262 100644 --- a/src/htm/algorithms/TemporalMemory.hpp +++ b/src/htm/algorithms/TemporalMemory.hpp @@ -104,7 +104,8 @@ class TemporalMemory : public Serializable * something like 4% * 0.01 = 0.0004). * * @param seed - * Seed for the random number generator. + * Seed for the random number generator. 0 (default) means truly random, + * use > 0 for fixed pseudo-random. * * @param maxSegmentsPerCell * The maximum number of segments per cell. @@ -144,7 +145,7 @@ class TemporalMemory : public Serializable Permanence permanenceIncrement = 0.10, Permanence permanenceDecrement = 0.10, Permanence predictedSegmentDecrement = 0.0, - Int seed = 42, + UInt seed = 0, //random SegmentIdx maxSegmentsPerCell = 255, SynapseIdx maxSynapsesPerSegment = 255, bool checkInputs = true, @@ -164,7 +165,7 @@ class TemporalMemory : public Serializable Permanence permanenceIncrement = 0.10, Permanence permanenceDecrement = 0.10, Permanence predictedSegmentDecrement = 0.0, - Int seed = 42, + UInt seed = 0, SegmentIdx maxSegmentsPerCell = 255, SynapseIdx maxSynapsesPerSegment = 255, bool checkInputs = true, @@ -595,6 +596,10 @@ class TemporalMemory : public Serializable virtual bool operator==(const TemporalMemory &other) const; inline bool operator!=(const TemporalMemory &other) const { return not this->operator==(other); } + void setSeed(UInt seed) { + rng_ = Random(seed); + } + //---------------------------------------------------------------------- // Debugging helpers //---------------------------------------------------------------------- @@ -638,6 +643,9 @@ class TemporalMemory : public Serializable * */ SDR cellsToColumns(const SDR& cells) const; + + + private: void punishPredictedColumn_(vector::const_iterator columnMatchingSegmentsBegin, vector::const_iterator columnMatchingSegmentsEnd, diff --git a/src/htm/regions/RDSEEncoderRegion.cpp b/src/htm/regions/RDSEEncoderRegion.cpp index 6a7f5848d4..b383fbe515 100644 --- a/src/htm/regions/RDSEEncoderRegion.cpp +++ b/src/htm/regions/RDSEEncoderRegion.cpp @@ -68,7 +68,6 @@ namespace htm { RDSEEncoderRegion::RDSEEncoderRegion(const ValueMap &par, Region *region) : RegionImpl(region) { - rnd_ = Random(42); spec_.reset(createSpec()); ValueMap params = ValidateParameters(par, spec_.get()); @@ -78,10 +77,11 @@ RDSEEncoderRegion::RDSEEncoderRegion(const ValueMap &par, Region *region) : Regi args.sparsity = params.getScalarT("sparsity"); args.radius = params.getScalarT("radius"); args.resolution = params.getScalarT("resolution"); - args.category = params.getScalarT("category"); - args.seed = params.getScalarT("seed"); + args.category = params.getScalarT("category", false); + args.seed = params.getScalarT("seed", 0); encoder_ = std::make_shared(args); + rnd_ = Random(encoder_->parameters.seed); sensedValue_ = params.getScalarT("sensedValue"); noise_ = params.getScalarT("noise"); } diff --git a/src/htm/regions/SPRegion.cpp b/src/htm/regions/SPRegion.cpp index d973c7b6eb..7965dd88b2 100644 --- a/src/htm/regions/SPRegion.cpp +++ b/src/htm/regions/SPRegion.cpp @@ -55,7 +55,7 @@ SPRegion::SPRegion(const ValueMap &values, Region *region) args_.minPctOverlapDutyCycles = values.getScalarT("minPctOverlapDutyCycles", 0.001f); args_.dutyCyclePeriod = values.getScalarT("dutyCyclePeriod", 1000); args_.boostStrength = values.getScalarT("boostStrength", 0.0f); - args_.seed = values.getScalarT("seed", 1); + args_.seed = values.getScalarT("seed", 0u); args_.spVerbosity = values.getScalarT("spVerbosity", 0); args_.wrapAround = values.getScalarT("wrapAround", true); spatialImp_ = values.getString("spatialImp", ""); @@ -452,11 +452,11 @@ Spec *SPRegion::createSpec() { "seed", ParameterSpec( "(int)\n" - "Seed for our own pseudo - random number generator. Default ``-1``.", + "Seed for our own pseudo - random number generator. Default ``0``(=random), >0 means fixed.", NTA_BasicType_Int32, // type 1, // elementCount "", // constraints - "1", // defaultValue + "0", // defaultValue ParameterSpec::CreateAccess)); // access ns->parameters.add( diff --git a/src/htm/regions/SPRegion.hpp b/src/htm/regions/SPRegion.hpp index 27afaf8e9d..6753311326 100644 --- a/src/htm/regions/SPRegion.hpp +++ b/src/htm/regions/SPRegion.hpp @@ -174,7 +174,7 @@ class SPRegion : public RegionImpl, Serializable Real minPctOverlapDutyCycles; UInt dutyCyclePeriod; Real boostStrength; - Int seed; + UInt seed; UInt spVerbosity; bool wrapAround; bool learningMode; diff --git a/src/htm/regions/TMRegion.cpp b/src/htm/regions/TMRegion.cpp index 0e0da5189e..404253c7f2 100644 --- a/src/htm/regions/TMRegion.cpp +++ b/src/htm/regions/TMRegion.cpp @@ -52,7 +52,7 @@ TMRegion::TMRegion(const ValueMap ¶ms, Region *region) args_.permanenceIncrement = params.getScalarT("permanenceIncrement", 0.10f); args_.permanenceDecrement = params.getScalarT("permanenceDecrement", 0.10f); args_.predictedSegmentDecrement = params.getScalarT("predictedSegmentDecrement", 0.0f); - args_.seed = params.getScalarT("seed", 42); + args_.seed = params.getScalarT("seed", 0); args_.maxSegmentsPerCell = params.getScalarT("maxSegmentsPerCell", 255u); args_.maxSynapsesPerSegment = params.getScalarT("maxSynapsesPerSegment", 255u); args_.checkInputs = params.getScalarT("checkInputs", true); @@ -423,11 +423,11 @@ Spec *TMRegion::createSpec() { "seed", ParameterSpec("(int) Random number generator seed. The seed affects the random " "aspects of initialization like the initial permanence values. A " - "fixed value ensures a reproducible result.", + "fixed value > 0 ensures a reproducible result.", NTA_BasicType_Int32, // type 1, // elementCount "", // constraints - "42", // defaultValue + "0", // defaultValue ParameterSpec::CreateAccess)); // access ///////// Parameters not part of the calling arguments ////////// diff --git a/src/htm/regions/TMRegion.hpp b/src/htm/regions/TMRegion.hpp index 7b374707a4..9475a6621b 100644 --- a/src/htm/regions/TMRegion.hpp +++ b/src/htm/regions/TMRegion.hpp @@ -165,7 +165,7 @@ class TMRegion : public RegionImpl, Serializable { Real32 permanenceIncrement; Real32 permanenceDecrement; Real32 predictedSegmentDecrement; - Int32 seed; + UInt32 seed; Int32 maxSegmentsPerCell; Int32 maxSynapsesPerSegment; UInt32 externalPredictiveInputs; diff --git a/src/test/unit/algorithms/ConnectionsTest.cpp b/src/test/unit/algorithms/ConnectionsTest.cpp index 453449b20b..1d698b1ead 100644 --- a/src/test/unit/algorithms/ConnectionsTest.cpp +++ b/src/test/unit/algorithms/ConnectionsTest.cpp @@ -792,16 +792,19 @@ TEST(ConnectionsTest, testSaveLoad) { c1.destroySegment(segment); computeSampleActivity(c1); + ASSERT_NE(c1, c2) << "shouldn't be eq"; { stringstream ss; c1.save(ss); c2.load(ss); } + if(c1 == c2) NTA_WARN << "nice"; - ASSERT_EQ(c1, c2); + ASSERT_EQ(c1, c2) << "Deserialized must be equal"; } + TEST(ConnectionsTest, testCreateSegmentOverflow) { const auto LIMIT = std::numeric_limits::max(); if(LIMIT <= 256) { //connections::Segment is too large (likely uint32), so this test would run, but memory @@ -819,6 +822,7 @@ TEST(ConnectionsTest, testCreateSegmentOverflow) { } } + TEST(ConnectionsTest, testCreateSynapseOverflow) { const auto LIMIT = std::numeric_limits::max(); if(LIMIT <= 256) { //connections::Synapse is too large (likely uint32), so this test would run, but memory @@ -837,6 +841,7 @@ TEST(ConnectionsTest, testCreateSynapseOverflow) { } } + TEST(ConnectionsTest, testTimeseries) { Connections C( 1, .5, true ); auto seg = C.createSegment(0); @@ -871,3 +876,16 @@ TEST(ConnectionsTest, testTimeseries) { ASSERT_TRUE( (synData.permanence == 0.0f) or (synData.permanence == 1.0f) ); } } + + +TEST(ConnectionsTest, testEquals) { + Connections c1(100, 0.5, false), c2(100, 0.5, false); + ASSERT_EQ(c1, c2) << "Conn Eq: 1"; + + setupSampleConnections(c1); //..creates some synapses. + ASSERT_NE(c1, c2) << "Conn Eq: 2"; + + setupSampleConnections(c2); + ASSERT_EQ(c1, c2) << "Conn Eq: 3"; + +} diff --git a/src/test/unit/algorithms/SpatialPoolerTest.cpp b/src/test/unit/algorithms/SpatialPoolerTest.cpp index 7a7ab8ceae..f78a2ea3e5 100644 --- a/src/test/unit/algorithms/SpatialPoolerTest.cpp +++ b/src/test/unit/algorithms/SpatialPoolerTest.cpp @@ -120,109 +120,12 @@ bool check_vector_eq(vector vec1, vector vec2) { return true; } -void check_spatial_eq(const SpatialPooler& sp1, const SpatialPooler& sp2) { - UInt numColumns = sp1.getNumColumns(); - UInt numInputs = sp2.getNumInputs(); - - ASSERT_TRUE(sp1.getNumColumns() == sp2.getNumColumns()); - ASSERT_TRUE(sp1.getNumInputs() == sp2.getNumInputs()); - ASSERT_TRUE(sp1.getPotentialRadius() == sp2.getPotentialRadius()); - ASSERT_TRUE(sp1.getPotentialPct() == sp2.getPotentialPct()); - ASSERT_TRUE(sp1.getGlobalInhibition() == sp2.getGlobalInhibition()); - ASSERT_TRUE(almost_eq(sp1.getLocalAreaDensity(), sp2.getLocalAreaDensity())); - ASSERT_TRUE(sp1.getStimulusThreshold() == sp2.getStimulusThreshold()); - ASSERT_TRUE(sp1.getDutyCyclePeriod() == sp2.getDutyCyclePeriod()); - ASSERT_TRUE(almost_eq(sp1.getBoostStrength(), sp2.getBoostStrength())); - ASSERT_TRUE(sp1.getIterationNum() == sp2.getIterationNum()); - ASSERT_TRUE(sp1.getIterationLearnNum() == sp2.getIterationLearnNum()); - ASSERT_TRUE(sp1.getSpVerbosity() == sp2.getSpVerbosity()); - ASSERT_TRUE(sp1.getWrapAround() == sp2.getWrapAround()); - ASSERT_TRUE(sp1.getUpdatePeriod() == sp2.getUpdatePeriod()); - cout << "check: " << sp1.getSynPermActiveInc() << " " - << sp2.getSynPermActiveInc() << endl; - ASSERT_TRUE(almost_eq(sp1.getSynPermActiveInc(), sp2.getSynPermActiveInc())); - ASSERT_TRUE( - almost_eq(sp1.getSynPermInactiveDec(), sp2.getSynPermInactiveDec())); - ASSERT_TRUE(almost_eq(sp1.getSynPermBelowStimulusInc(), - sp2.getSynPermBelowStimulusInc())); - ASSERT_TRUE(almost_eq(sp1.getSynPermConnected(), sp2.getSynPermConnected())); - ASSERT_TRUE(almost_eq(sp1.getMinPctOverlapDutyCycles(), - sp2.getMinPctOverlapDutyCycles())); - - auto boostFactors1 = new Real[numColumns]; - auto boostFactors2 = new Real[numColumns]; - sp1.getBoostFactors(boostFactors1); - sp2.getBoostFactors(boostFactors2); - ASSERT_TRUE(check_vector_eq(boostFactors1, boostFactors2, numColumns)); - delete[] boostFactors1; - delete[] boostFactors2; - - auto overlapDutyCycles1 = new Real[numColumns]; - auto overlapDutyCycles2 = new Real[numColumns]; - sp1.getOverlapDutyCycles(overlapDutyCycles1); - sp2.getOverlapDutyCycles(overlapDutyCycles2); - ASSERT_TRUE( - check_vector_eq(overlapDutyCycles1, overlapDutyCycles2, numColumns)); - delete[] overlapDutyCycles1; - delete[] overlapDutyCycles2; - - auto activeDutyCycles1 = new Real[numColumns]; - auto activeDutyCycles2 = new Real[numColumns]; - sp1.getActiveDutyCycles(activeDutyCycles1); - sp2.getActiveDutyCycles(activeDutyCycles2); - ASSERT_TRUE( - check_vector_eq(activeDutyCycles1, activeDutyCycles2, numColumns)); - delete[] activeDutyCycles1; - delete[] activeDutyCycles2; - - auto minOverlapDutyCycles1 = new Real[numColumns]; - auto minOverlapDutyCycles2 = new Real[numColumns]; - sp1.getMinOverlapDutyCycles(minOverlapDutyCycles1); - sp2.getMinOverlapDutyCycles(minOverlapDutyCycles2); - ASSERT_TRUE(check_vector_eq(minOverlapDutyCycles1, minOverlapDutyCycles2, - numColumns)); - delete[] minOverlapDutyCycles1; - delete[] minOverlapDutyCycles2; - - for (UInt i = 0; i < numColumns; i++) { - auto potential1 = new UInt[numInputs]; - auto potential2 = new UInt[numInputs]; - sp1.getPotential(i, potential1); - sp2.getPotential(i, potential2); - ASSERT_TRUE(check_vector_eq(potential1, potential2, numInputs)); - delete[] potential1; - delete[] potential2; - } - - // check get permanences - for (UInt i = 0; i < numColumns; i++) { - const auto& perm1 = sp1.getPermanence(i); - const auto& perm2 = sp2.getPermanence(i); - ASSERT_TRUE(check_vector_eq(perm1, perm2)); - } - - // check get connected synapses - for (UInt i = 0; i < numColumns; i++) { - const auto& con1 = sp1.getPermanence(i, sp1.connections.getConnectedThreshold()); - const auto& con2 = sp2.getPermanence(i, sp2.connections.getConnectedThreshold()); - ASSERT_TRUE(check_vector_eq(con1, con2)); - } - - auto conCounts1 = new UInt[numColumns]; - auto conCounts2 = new UInt[numColumns]; - sp1.getConnectedCounts(conCounts1); - sp2.getConnectedCounts(conCounts2); - ASSERT_TRUE(check_vector_eq(conCounts1, conCounts2, numColumns)); - delete[] conCounts1; - delete[] conCounts2; -} - void setup(SpatialPooler &sp, vector inputDim, vector columnDim, Real sparsity = 0.5f) { //we are interested in the sparsity, should make it artificially high. //As we added SP check that sparsity*numColumns > 0, which is correct requirement. //But many tests have very small (artificial) number of columns (for convenient results), //therefore the check is failing -> we must set high sparsity at initialization. - EXPECT_NO_THROW(sp.initialize(inputDim, columnDim, 16u, 0.5f, true, sparsity)); + EXPECT_NO_THROW(sp.initialize(inputDim, columnDim, 16u, 0.5f, true, sparsity)) << "SP test: failed to initialize() SP"; } void setup(SpatialPooler& sp, UInt numIn, UInt numCols, Real sparsity = 0.5f) { setup(sp, vector{numIn}, vector{numCols}, sparsity); @@ -234,11 +137,11 @@ TEST(SpatialPoolerTest, testUpdateInhibitionRadius) { colDim.push_back(57); colDim.push_back(31); colDim.push_back(2); - inputDim.push_back(1); + inputDim.push_back(100); inputDim.push_back(1); inputDim.push_back(1); - EXPECT_NO_THROW(sp.initialize(inputDim, colDim)); + EXPECT_NO_THROW(sp.initialize(inputDim, colDim, /*potentialRadius: must be <= numInputs_ */16u)); sp.setGlobalInhibition(true); ASSERT_EQ(sp.getInhibitionRadius(), 57u); @@ -1843,6 +1746,9 @@ TEST(SpatialPoolerTest, testSaveLoad) { sp1.save(outfile); outfile.close(); + EXPECT_NE(sp1, sp2); + + // now deserialize to the sp2 ifstream infile(filename, ifstream::binary); sp2.load(infile); infile.close(); @@ -1850,7 +1756,7 @@ TEST(SpatialPoolerTest, testSaveLoad) { int ret = ::remove(filename); ASSERT_TRUE(ret == 0) << "Failed to delete " << filename; - check_spatial_eq(sp1, sp2); + EXPECT_EQ(sp1, sp2) << "Deserialized SP must be the same!"; } @@ -1875,8 +1781,8 @@ TEST(SpatialPoolerTest, testSerialization2) { // Save initial trained model ofstream osC("outC.stream", ofstream::binary); - osC.precision(std::numeric_limits::digits10 + 1); - osC.precision(std::numeric_limits::digits10 + 1); +// osC.precision(std::numeric_limits::digits10 + 1); +// osC.precision(std::numeric_limits::digits10 + 1); sp1.save(osC); osC.close(); @@ -1906,8 +1812,8 @@ TEST(SpatialPoolerTest, testSerialization2) { // Serialize ofstream os("outC.stream", ofstream::binary); - os.precision(std::numeric_limits::digits10 + 1); - os.precision(std::numeric_limits::digits10 + 1); +// os.precision(std::numeric_limits::digits10 + 1); +// os.precision(std::numeric_limits::digits10 + 1); spTemp.save(os); os.close(); @@ -1936,7 +1842,7 @@ TEST(SpatialPoolerTest, testSaveLoad_ar) { int ret = ::remove(filename); ASSERT_TRUE(ret == 0) << "Failed to delete " << filename; - check_spatial_eq(sp1, sp2); + EXPECT_EQ(sp1, sp2); } @@ -1952,62 +1858,53 @@ TEST(SpatialPoolerTest, testSerialization_ar) { SpatialPooler sp1; sp1.initialize(inputDims, colDims); + sp1.setSeed(1); + ASSERT_EQ(sp1.getSeed(), 1u); SDR input(inputDims); SDR output(colDims); + //burn-in the SP for (UInt i = 0; i < 100; ++i) { input.randomize(0.05f, random); //5% random ON sp1.compute(input, true, output); } - // Now we reuse the last input to test after serialization - - auto activeColumnsBefore = output.getSparse(); // Save initial trained model stringstream ss; - ss.precision(std::numeric_limits::digits10 + 1); - ss.precision(std::numeric_limits::digits10 + 1); - sp1.save(ss); +// ss.precision(std::numeric_limits::digits10 + 1); +// ss.precision(std::numeric_limits::digits10 + 1); + EXPECT_NO_THROW(sp1.save(ss)) << "serializing failed"; - SpatialPooler sp2; - htm::Timer testTimer; - - for (UInt i = 0; i < 6; ++i) { - // Create new input - input.randomize(0.05f, random); - - // Get expected output - SDR outputBaseline(output); - sp1.compute(input, true, outputBaseline); - - // C - Next do old version + // C - Next, verify the same results come from the de/serialized version { + // Deserialize: SpatialPooler spTemp; - testTimer.start(); + //ss.seekg(0); + EXPECT_NO_THROW(spTemp.load(ss)); + ASSERT_EQ(spTemp.getSeed(), 1u); + ASSERT_EQ(sp1, spTemp) << "Loaded SP is not the same as original"; //equals method used in SP - // Deserialize - ss.seekg(0); - spTemp.load(ss); + // 1 step: Create new input & compute SP output + for(int i=0; i < 42; i++) { + input.randomize(0.05f, random); + SDR expected(colDims); + sp1.compute(input, true, expected); - // Feed new record through - SDR outputC({numColumns}); - spTemp.compute(input, true, outputC); + // Feed new record through + SDR outputC(colDims); + spTemp.compute(input, true, outputC); - // Serialize - ss.clear(); - spTemp.save(ss); - testTimer.stop(); + EXPECT_EQ(expected, outputC) << "Output of original and deserialized SP must be the same"; + } - EXPECT_EQ(outputBaseline, outputC); + // Serialize: + ss.clear(); + EXPECT_NO_THROW(spTemp.save(ss)); } - } - ss.clear(); - - cout << "[ ] Timing for SP serialization: " << testTimer.getElapsed() << "sec" << endl; } @@ -2032,6 +1929,9 @@ TEST(SpatialPoolerTest, testConstructorVsInitialize) { /*spVerbosity*/ 0, /*wrapAround*/ true); + const SpatialPooler spCopy = sp1; + EXPECT_EQ(sp1, spCopy) << "Copy constructor should be equal"; + // Initialize SP using the "initialize" method SpatialPooler sp2; sp2.initialize( @@ -2054,9 +1954,7 @@ TEST(SpatialPoolerTest, testConstructorVsInitialize) { /*wrapAround*/ true); // The two SP should be the same - check_spatial_eq(sp1, sp2); - EXPECT_EQ(sp1, sp2); - EXPECT_TRUE(sp1 == sp2) << "Spatial Poolers not equal"; + EXPECT_EQ(sp1, sp2) << "Spatial Poolers not equal"; } diff --git a/src/test/unit/engine/CppRegionTest.cpp b/src/test/unit/engine/CppRegionTest.cpp index 5c3def9db1..e5053dee06 100644 --- a/src/test/unit/engine/CppRegionTest.cpp +++ b/src/test/unit/engine/CppRegionTest.cpp @@ -110,7 +110,7 @@ TEST(CppRegionTest, testCppLinkingSDR) { Network net; std::shared_ptr region1 = net.addRegion("region1", "ScalarEncoderRegion", "{dim: [6,1], n: 6, w: 2}"); - std::shared_ptr region2 = net.addRegion("region2", "SPRegion", "{dim: [20,3]}"); + std::shared_ptr region2 = net.addRegion("region2", "SPRegion", "{dim: [20,3], seed: 1}"); //seed is fixed for deterministic results in tests net.link("region1", "region2"); diff --git a/src/test/unit/regions/ClassifierRegionTest.cpp b/src/test/unit/regions/ClassifierRegionTest.cpp index 689d7ccaae..e27aada24f 100644 --- a/src/test/unit/regions/ClassifierRegionTest.cpp +++ b/src/test/unit/regions/ClassifierRegionTest.cpp @@ -101,7 +101,7 @@ TEST(ClassifierRegionTest, asCategoryDecoder) { Network net; std::shared_ptr encoder = net.addRegion("encoder", "RDSEEncoderRegion", "{size: 400, seed: 42, category: true, activeBits: 40}"); - std::shared_ptr sp = net.addRegion("sp", "SPRegion", "{columnCount: 1000, globalInhibition: true}"); + std::shared_ptr sp = net.addRegion("sp", "SPRegion", "{columnCount: 1000, globalInhibition: true, seed: 1}"); std::shared_ptr classifier = net.addRegion("classifier", "ClassifierRegion", "{learn: true}"); net.link("encoder", "sp", "", "", "encoded", "bottomUpIn"); @@ -144,7 +144,7 @@ TEST(ClassifierRegionTest, asRealDecoder) { Network net; std::shared_ptr encoder = net.addRegion("encoder", "RDSEEncoderRegion", "{size: 400, radius: 0.1, seed: 42, activeBits: 40}"); - std::shared_ptr sp = net.addRegion("sp", "SPRegion", "{columnCount: 1000, globalInhibition: true}"); + std::shared_ptr sp = net.addRegion("sp", "SPRegion", "{columnCount: 1000, globalInhibition: true, seed: 1}"); std::shared_ptr classifier = net.addRegion("classifier", "ClassifierRegion", "{learn: true}"); net.link("encoder", "sp", "", "", "encoded", "bottomUpIn"); diff --git a/src/test/unit/regions/SPRegionTest.cpp b/src/test/unit/regions/SPRegionTest.cpp index 4abd7f628b..416cc42040 100644 --- a/src/test/unit/regions/SPRegionTest.cpp +++ b/src/test/unit/regions/SPRegionTest.cpp @@ -400,7 +400,7 @@ TEST(SPRegionTest, testGetParameters) "minPctOverlapDutyCycles": 0.001000, "dutyCyclePeriod": 1000, "boostStrength": 0.000000, - "seed": 1, + "seed": 0, "spVerbosity": 0, "wrapAround": true, "learningMode": 1, @@ -429,7 +429,7 @@ TEST(SPRegionTest, testGetParameters) "minPctOverlapDutyCycles": 0.001000, "dutyCyclePeriod": 1000, "boostStrength": 0.000000, - "seed": 1, + "seed": 0, "spVerbosity": 0, "wrapAround": true, "learningMode": 1, diff --git a/src/test/unit/regions/TMRegionTest.cpp b/src/test/unit/regions/TMRegionTest.cpp index 0999be7615..e95a71ad45 100644 --- a/src/test/unit/regions/TMRegionTest.cpp +++ b/src/test/unit/regions/TMRegionTest.cpp @@ -211,9 +211,9 @@ TEST(TMRegionTest, testLinking) { // you can use JSON format as well) std::string parameters = "{activeOutputCount: " + std::to_string(dataWidth) + "}"; std::shared_ptr region1 = net.addRegion("region1", "FileInputRegion",parameters); - std::shared_ptr region2 = net.addRegion("region2", "SPRegion", "{dim: [2,10]}"); + std::shared_ptr region2 = net.addRegion("region2", "SPRegion", "{dim: [2,10], seed: 1}"); std::shared_ptr region3 = net.addRegion("region3", "TMRegion", - "{activationThreshold: 11, cellsPerColumn: 5}"); + "{activationThreshold: 11, cellsPerColumn: 5, seed: 42}"); std::shared_ptr region4 = net.addRegion("region4", "FileOutputRegion", "{outputFile: '" + test_output_file + "'}"); @@ -446,7 +446,7 @@ TEST(TMRegionTest, testGetParameters) { "predictedSegmentDecrement": 0.000000, "maxSegmentsPerCell": 255, "maxSynapsesPerSegment": 255, - "seed": 42, + "seed": 0, "inputWidth": 0, "learningMode": true, "activeOutputCount": 0, @@ -472,7 +472,7 @@ TEST(TMRegionTest, testGetParameters) { "predictedSegmentDecrement": 0.000000, "maxSegmentsPerCell": 255, "maxSynapsesPerSegment": 255, - "seed": 42, + "seed": 0, "inputWidth": 100, "learningMode": true, "activeOutputCount": 0,