Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cereal for TMRegion #421

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions external/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,7 @@ include(mnist_data.cmake)
# Cereal serialization package
include(cereal.cmake)

##################
# MurmurHash3
include(murmurhash.cmake)

2 changes: 1 addition & 1 deletion src/examples/hotgym/HelloSPTP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ Real64 BenchmarkHotgym::run(UInt EPOCHS, bool useSPlocal, bool useSPglobal, bool
if(EPOCHS == 5000) { //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;
NTA_CHECK(outSPglobal == goldSP) << "Deterministic output of SP (g) failed!\n" << outSP << "should be:\n" << goldSP;
NTA_CHECK(outSPlocal == goldSPlocal) << "Deterministic output of SP (l) failed!\n" << outSPlocal << "should be:\n" << goldSPlocal;
NTA_CHECK(outSPlocal == goldSPlocal) << "Deterministic output of SP (l) failed!\n" << outSPlocal << "should be:\n" << goldSPlocal;
NTA_CHECK(outTM == goldTM) << "Deterministic output of TM failed!\n" << outTM << "should be:\n" << goldTM;
NTA_CHECK(static_cast<UInt>(an *10000.0f) == static_cast<UInt>(goldAn *10000.0f)) //compare to 4 decimal places
<< "Deterministic output of Anomaly failed! " << an << "should be: " << goldAn;
Expand Down
51 changes: 1 addition & 50 deletions src/nupic/algorithms/Connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -557,42 +557,6 @@ void Connections::bumpSegment(const Segment segment, const Permanence delta) {
}


void Connections::destroyMinPermanenceSynapses(
const Segment segment, Int nDestroy,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is some mistake, was added by #446

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

..all the connections changes seem out of place. try git checkout -- master src/nupic/..../Connections.*

const vector<CellIdx> &excludeCells)
{
NTA_ASSERT( nDestroy >= 0 );
if( nDestroy <= 0 ) return; // Nothing to do.

// Don't destroy any cells that are in excludeCells.
vector<Synapse> destroyCandidates;
for( Synapse synapse : synapsesForSegment(segment)) {
const CellIdx presynapticCell = dataForSynapse(synapse).presynapticCell;

if( not std::binary_search(excludeCells.cbegin(), excludeCells.cend(), presynapticCell)) {
destroyCandidates.push_back(synapse);
}
}

const auto comparePermanences = [&](const Synapse A, const Synapse B) {
const Permanence A_perm = dataForSynapse(A).permanence;
const Permanence B_perm = dataForSynapse(B).permanence;
if( A_perm == B_perm ) {
return A < B;
}
else {
return A_perm < B_perm;
}
};
std::sort(destroyCandidates.begin(), destroyCandidates.end(), comparePermanences);

nDestroy = std::min( nDestroy, (Int) destroyCandidates.size() );
for(Int i = 0; i < nDestroy; i++) {
destroySynapse( destroyCandidates[i] );
}
}


namespace nupic {
namespace algorithms {
namespace connections {
Expand All @@ -604,9 +568,6 @@ std::ostream& operator<< (std::ostream& stream, const Connections& self)
<< ") ~> Outputs (" << self.cells_.size()
<< ") via Segments (" << self.numSegments() << ")" << std::endl;

UInt segmentsMin = -1;
Real segmentsMean = 0.0f;
UInt segmentsMax = 0u;
UInt potentialMin = -1;
Real potentialMean = 0.0f;
UInt potentialMax = 0;
Expand All @@ -615,16 +576,9 @@ std::ostream& operator<< (std::ostream& stream, const Connections& self)
SynapseIdx connectedMax = 0;
UInt synapsesDead = 0;
UInt synapsesSaturated = 0;
for( const auto cellData : self.cells_ )
{
const UInt numSegments = (UInt) cellData.segments.size();
segmentsMin = std::min( segmentsMin, numSegments );
segmentsMax = std::max( segmentsMax, numSegments );
segmentsMean += numSegments;

for( const auto cellData : self.cells_ ) {
for( const auto seg : cellData.segments ) {
const auto &segData = self.dataForSegment( seg );

const UInt numPotential = (UInt) segData.synapses.size();
potentialMin = std::min( potentialMin, numPotential );
potentialMax = std::max( potentialMax, numPotential );
Expand All @@ -643,12 +597,9 @@ std::ostream& operator<< (std::ostream& stream, const Connections& self)
}
}
}
segmentsMean = segmentsMean / self.numCells();
potentialMean = potentialMean / self.numSegments();
connectedMean = connectedMean / self.numSegments();

stream << " Segments on Cell Min/Mean/Max "
<< segmentsMin << " / " << segmentsMean << " / " << segmentsMax << std::endl;
stream << " Potential Synapses on Segment Min/Mean/Max "
<< potentialMin << " / " << potentialMean << " / " << potentialMax << std::endl;
stream << " Connected Synapses on Segment Min/Mean/Max "
Expand Down
11 changes: 0 additions & 11 deletions src/nupic/algorithms/Connections.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,17 +449,6 @@ class Connections : public Serializable
*/
void bumpSegment(const Segment segment, const Permanence delta);

/**
* Destroy the synapses with the lowest permanence values. This method is
* useful for making room for more synapses on a segment which is already
* full.
*
* @param segment - Index of segment in Connections, to be modified.
* @param nDestroy - Must be greater than or equal to zero!
* @param excludeCells - Presynaptic cells which will NOT have any synapses destroyed.
*/
void destroyMinPermanenceSynapses(const Segment segment, Int nDestroy,
const sdr::SDR_sparse_t &excludeCells = {});

/**
* Print diagnostic info
Expand Down
41 changes: 40 additions & 1 deletion src/nupic/algorithms/TemporalMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,45 @@ static void adaptSegment(Connections &connections, Segment segment,
}
}

static void destroyMinPermanenceSynapses(Connections &connections, Random &rng,
Segment segment, Int nDestroy,
const vector<CellIdx> &excludeCells) {
// Don't destroy any cells that are in excludeCells.
vector<Synapse> destroyCandidates;
for (Synapse synapse : connections.synapsesForSegment(segment)) {
const CellIdx presynapticCell =
connections.dataForSynapse(synapse).presynapticCell;

if (!std::binary_search(excludeCells.begin(), excludeCells.end(),
presynapticCell)) {
destroyCandidates.push_back(synapse);
}
}

// Find cells one at a time. This is slow, but this code rarely runs, and it
// needs to work around floating point differences between environments.
for (Int32 i = 0; i < nDestroy && !destroyCandidates.empty(); i++) {
Permanence minPermanence = std::numeric_limits<Permanence>::max();
vector<Synapse>::iterator minSynapse = destroyCandidates.end();

for (auto synapse = destroyCandidates.begin();
synapse != destroyCandidates.end(); synapse++) {
const Permanence permanence =
connections.dataForSynapse(*synapse).permanence;

// Use special Epsilon logic to compensate for floating point
// differences between C++ and other environments.
if (permanence < minPermanence - nupic::Epsilon) {
minSynapse = synapse;
minPermanence = permanence;
}
}

connections.destroySynapse(*minSynapse);
destroyCandidates.erase(minSynapse);
}
}

static void growSynapses(Connections &connections,
Random &rng,
const Segment& segment,
Expand Down Expand Up @@ -240,7 +279,7 @@ static void growSynapses(Connections &connections,
// Check if we're going to surpass the maximum number of synapses.
Int overrun = static_cast<Int>(connections.numSynapses(segment) + nActual - maxSynapsesPerSegment);
if (overrun > 0) {
connections.destroyMinPermanenceSynapses(segment, static_cast<Int>(overrun), prevWinnerCells);
destroyMinPermanenceSynapses(connections, rng, segment, overrun, prevWinnerCells);
}

// Recalculate in case we weren't able to destroy as many synapses as needed.
Expand Down
2 changes: 1 addition & 1 deletion src/nupic/engine/Region.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ bool Region::isParameter(const std::string &name) const {
return (spec_->parameters.contains(name));
}

// Some functions used to prevent symbles from being in Region.hpp
// Some functions used to prevent symbols from being in Region.hpp
void Region::getDims_(std::map<std::string,Dimensions>& outDims,
std::map<std::string,Dimensions>& inDims) const {
for(auto out: outputs_) {
Expand Down
64 changes: 64 additions & 0 deletions src/nupic/regions/TMRegion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,70 @@ class TMRegion : public RegionImpl, Serializable {
void serialize(BundleIO &bundle) override;
void deserialize(BundleIO &bundle) override;

CerealAdapter; // see Serializable.hpp
// FOR Cereal Serialization
template<class Archive>
void save_ar(Archive& ar) const {
bool init = ((tm_) ? true : false);
ar(cereal::make_nvp("numberOfCols", args_.numberOfCols));
ar(cereal::make_nvp("cellsPerColumn", args_.cellsPerColumn));
ar(cereal::make_nvp("activationThreshold", args_.activationThreshold));
ar(cereal::make_nvp("initialPermanence", args_.initialPermanence));
ar(cereal::make_nvp("connectedPermanence", args_.connectedPermanence));
ar(cereal::make_nvp("maxNewSynapseCount", args_.maxNewSynapseCount));
ar(cereal::make_nvp("permanenceIncrement", args_.permanenceIncrement));
ar(cereal::make_nvp("permanenceDecrement", args_.permanenceDecrement));
ar(cereal::make_nvp("predictedSegmentDecrement", args_.predictedSegmentDecrement));
ar(cereal::make_nvp("seed", args_.seed));
ar(cereal::make_nvp("maxSegmentsPerCell", args_.maxSegmentsPerCell));
ar(cereal::make_nvp("maxSynapsesPerSegment", args_.maxSynapsesPerSegment));
ar(cereal::make_nvp("extra", args_.extra));
ar(cereal::make_nvp("checkInputs", args_.checkInputs));
ar(cereal::make_nvp("learningMode", args_.learningMode));
ar(cereal::make_nvp("sequencePos", args_.sequencePos));
ar(cereal::make_nvp("iter", args_.iter));
ar(cereal::make_nvp("orColumnOutputs", args_.orColumnOutputs));
ar(cereal::make_nvp("dim", dim_)); // from RegionImpl
ar(cereal::make_nvp("init", init));
if (init) {
// Save the algorithm state
ar(cereal::make_nvp("TM", tm_));
}
}

// FOR Cereal Deserialization
template<class Archive>
void load_ar(Archive& ar) {
ar(cereal::make_nvp("numberOfCols", args_.numberOfCols));
ar(cereal::make_nvp("cellsPerColumn", args_.cellsPerColumn));
ar(cereal::make_nvp("activationThreshold", args_.activationThreshold));
ar(cereal::make_nvp("initialPermanence", args_.initialPermanence));
ar(cereal::make_nvp("connectedPermanence", args_.connectedPermanence));
ar(cereal::make_nvp("maxNewSynapseCount", args_.maxNewSynapseCount));
ar(cereal::make_nvp("permanenceIncrement", args_.permanenceIncrement));
ar(cereal::make_nvp("permanenceDecrement", args_.permanenceDecrement));
ar(cereal::make_nvp("predictedSegmentDecrement", args_.predictedSegmentDecrement));
ar(cereal::make_nvp("seed", args_.seed));
ar(cereal::make_nvp("maxSegmentsPerCell", args_.maxSegmentsPerCell));
ar(cereal::make_nvp("maxSynapsesPerSegment", args_.maxSynapsesPerSegment));
ar(cereal::make_nvp("extra", args_.extra));
ar(cereal::make_nvp("checkInputs", args_.checkInputs));
ar(cereal::make_nvp("learningMode", args_.learningMode));
ar(cereal::make_nvp("sequencePos", args_.sequencePos));
ar(cereal::make_nvp("iter", args_.iter));
ar(cereal::make_nvp("orColumnOutputs", args_.orColumnOutputs));
args_.outputWidth = (args_.orColumnOutputs)?args_.numberOfCols
: (args_.numberOfCols * args_.cellsPerColumn);
ar(cereal::make_nvp("dim", dim_)); // from RegionImpl
ar(cereal::make_nvp("init", args_.init));
if (args_.init) {
// Restore algorithm state
nupic::algorithms::temporal_memory::TemporalMemory* tm = new nupic::algorithms::temporal_memory::TemporalMemory();
tm_.reset(tm);
ar(cereal::make_nvp("TM", tm_));
}
}

// Per-node size (in elements) of the given output.
// For per-region outputs, it is the total element count.
// This method is called only for outputs whose size is not
Expand Down
9 changes: 0 additions & 9 deletions src/test/unit/regions/SPRegionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,6 @@ namespace testing
<< "actual type is " << BasicType::getName(r1OutputArray.getType());

Real32 *buffer1 = (Real32*) r1OutputArray.getBuffer();
//for (size_t i = 0; i < r1OutputArray.getCount(); i++)
//{
//VERBOSE << " [" << i << "]= " << buffer1[i] << "" << std::endl;
//}

VERBOSE << " SPRegion input" << std::endl;
Array r2InputArray = region2->getInputData("bottomUpIn");
Expand Down Expand Up @@ -324,11 +320,6 @@ TEST(SPRegionTest, testSerialization)
std::map<std::string, std::string> parameterMap;
EXPECT_TRUE(captureParameters(n1region2, parameterMap)) << "Capturing parameters before save.";

// TODO: JSON serialization does not work.
// returns 3 (not really a crash)
// It fails returning from SpatialPooler, in rapidjson::PrettyWriter.h line 128
// It is apparently checking that it is not in array mode.

Directory::removeTree("TestOutputDir", true);
VERBOSE << "Writing stream to " << Path::makeAbsolute("TestOutputDir/spRegionTest.stream") << "\n";
net1.saveToFile_ar("TestOutputDir/spRegionTest.stream", SerializableFormat::JSON);
Expand Down
Loading