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

SP compute() const, thread safe #560

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions src/htm/algorithms/Connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ void Connections::reset()
currentUpdates_.clear();
}

vector<SynapseIdx> Connections::computeActivity(const vector<CellIdx> &activePresynapticCells, const bool learn) {
vector<SynapseIdx> Connections::computeActivity(const vector<CellIdx> &activePresynapticCells, const bool learn) const {

vector<SynapseIdx> numActiveConnectedSynapsesForSegment(segments_.size(), 0);
if(learn) iteration_++;
Expand All @@ -406,7 +406,7 @@ vector<SynapseIdx> Connections::computeActivity(const vector<CellIdx> &activePre
vector<SynapseIdx> Connections::computeActivity(
vector<SynapseIdx> &numActivePotentialSynapsesForSegment,
const vector<CellIdx> &activePresynapticCells,
const bool learn) {
const bool learn) const {
NTA_ASSERT(numActivePotentialSynapsesForSegment.size() == segments_.size());

// Iterate through all connected synapses.
Expand Down
8 changes: 4 additions & 4 deletions src/htm/algorithms/Connections.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,10 +441,10 @@ class Connections : public Serializable
std::vector<SynapseIdx> computeActivity(
std::vector<SynapseIdx> &numActivePotentialSynapsesForSegment,
const std::vector<CellIdx> &activePresynapticCells,
const bool learn = true);
const bool learn = true) const;

std::vector<SynapseIdx> computeActivity(const std::vector<CellIdx> &activePresynapticCells,
const bool learn = true);
const bool learn = true) const;

/**
* The primary method in charge of learning. Adapts the permanence values of
Expand Down Expand Up @@ -730,8 +730,8 @@ class Connections : public Serializable
// These three members should be used when working with highly correlated
// data. The vectors store the permanence changes made by adaptSegment.
bool timeseries_;
std::vector<Permanence> previousUpdates_;
std::vector<Permanence> currentUpdates_;
mutable std::vector<Permanence> previousUpdates_; //FIXME use lock or async vector (from folly?)
mutable std::vector<Permanence> currentUpdates_;

//for prune statistics
Synapse prunedSyns_ = 0; //how many synapses have been removed?
Expand Down
57 changes: 12 additions & 45 deletions src/htm/algorithms/SpatialPooler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,6 @@ void SpatialPooler::setBoostStrength(Real boostStrength) {
boostStrength_ = boostStrength;
}

UInt SpatialPooler::getIterationNum() const { return iterationNum_; }

void SpatialPooler::setIterationNum(UInt iterationNum) {
Copy link
Member Author

Choose a reason for hiding this comment

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

SP removed iteration stuff. not really needed. And connections will have the iteration from #537

iterationNum_ = iterationNum;
}

UInt SpatialPooler::getIterationLearnNum() const { return iterationLearnNum_; }

void SpatialPooler::setIterationLearnNum(UInt iterationLearnNum) {
iterationLearnNum_ = iterationLearnNum;
}

UInt SpatialPooler::getSpVerbosity() const { return spVerbosity_; }

void SpatialPooler::setSpVerbosity(UInt spVerbosity) {
Expand Down Expand Up @@ -359,10 +347,6 @@ void SpatialPooler::getConnectedCounts(UInt connectedCounts[]) const {
}


const vector<Real> &SpatialPooler::getBoostedOverlaps() const {
return boostedOverlaps_;
}

void SpatialPooler::initialize(
const vector<UInt>& inputDimensions,
const vector<UInt>& columnDimensions,
Expand Down Expand Up @@ -422,14 +406,11 @@ void SpatialPooler::initialize(
wrapAround_ = wrapAround;
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;
iterationLearnNum_ = 0u;

overlapDutyCycles_.assign(numColumns_, 0); //TODO make all these sparse or rm to reduce footprint
activeDutyCycles_.assign(numColumns_, 0);
minOverlapDutyCycles_.assign(numColumns_, 0.0);
boostFactors_.assign(numColumns_, 1.0); //1 is neutral value for boosting
boostedOverlaps_.resize(numColumns_);

inhibitionRadius_ = 0;

Expand Down Expand Up @@ -457,17 +438,16 @@ void SpatialPooler::initialize(
}


const vector<SynapseIdx> SpatialPooler::compute(const SDR &input, const bool learn, SDR &active) {
vector<SynapseIdx> SpatialPooler::compute(const SDR &input, const bool learn, SDR &active) const {
Copy link
Member Author

Choose a reason for hiding this comment

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

WIP compute() is const.
But that will not be feasible. (adaptSegment -> conn.updateSynapsePermanence)

input.reshape( inputDimensions_ );
active.reshape( columnDimensions_ );
updateBookeepingVars_(learn);

//now calculate overlaps of input and input synapses
const auto& overlaps = connections_.computeActivity(input.getSparse(), learn);

boostOverlaps_(overlaps, boostedOverlaps_);
const auto& boostedOverlaps = getBoostedOverlaps(overlaps);

auto &activeVector = active.getSparse();
inhibitColumns_(boostedOverlaps_, activeVector);
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
// inplace.
Expand All @@ -484,20 +464,19 @@ const vector<SynapseIdx> SpatialPooler::compute(const SDR &input, const bool lea
updateMinDutyCycles_();
}
}

return overlaps;
}


void SpatialPooler::boostOverlaps_(const vector<SynapseIdx> &overlaps, //TODO use Eigen sparse vector here
vector<Real> &boosted) const {
vector<Real> SpatialPooler::getBoostedOverlaps(const vector<SynapseIdx> &overlaps) const { //TODO use Eigen sparse vector here
vector<Real> boosted(overlaps.begin(), overlaps.end()); //copy
if(boostStrength_ < htm::Epsilon) { //boost ~ 0.0, we can skip these computations, just copy the data
boosted.assign(overlaps.begin(), overlaps.end());
return;
return boosted;
}
for (UInt i = 0; i < numColumns_; i++) {
boosted[i] = overlaps[i] * boostFactors_[i];
boosted[i] = overlaps[i] * boostFactors_[i]; //TODO vectorize boosted * factors
}
return boosted;
}


Expand Down Expand Up @@ -646,7 +625,7 @@ void SpatialPooler::updateDutyCycles_(const vector<SynapseIdx> &overlaps,
}
newOverlap.setSparse( overlapsSparseVec );

const UInt period = std::min(dutyCyclePeriod_, iterationNum_);
const UInt period = dutyCyclePeriod_;

updateDutyCyclesHelper_(overlapDutyCycles_, newOverlap, period);
updateDutyCyclesHelper_(activeDutyCycles_, active, period);
Expand Down Expand Up @@ -792,14 +771,6 @@ void SpatialPooler::updateBoostFactorsLocal_() {
}


void SpatialPooler::updateBookeepingVars_(bool learn) {
iterationNum_++;
if (learn) {
iterationLearnNum_++;
}
}


void SpatialPooler::inhibitColumns_(const vector<Real> &overlaps,
vector<CellIdx> &activeColumns) const {
const Real density = localAreaDensity_;
Expand Down Expand Up @@ -909,7 +880,7 @@ void SpatialPooler::inhibitColumnsLocal_(const vector<Real> &overlaps,


bool SpatialPooler::isUpdateRound_() const {
return (iterationNum_ % updatePeriod_) == 0;
return (rng_.getReal64() < 1.0/updatePeriod_); //approx every updatePeriod steps
Copy link
Member Author

Choose a reason for hiding this comment

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

this is one place where iterationNum_ was needed, I have replaced with probability, which I think is nicer approach.

}

namespace htm {
Expand All @@ -927,9 +898,7 @@ std::ostream& operator<< (std::ostream& stream, const SpatialPooler& self)

// Print the main SP creation parameters
void SpatialPooler::printParameters(std::ostream& out) const {
out << "------------CPP SpatialPooler Parameters ------------------\n";
out << "iterationNum = " << getIterationNum() << std::endl
<< "iterationLearnNum = " << getIterationLearnNum() << std::endl
out << "------------CPP SpatialPooler Parameters ------------------\n"
<< "numInputs = " << getNumInputs() << std::endl
<< "numColumns = " << getNumColumns() << std::endl
<< std::endl
Expand Down Expand Up @@ -987,8 +956,6 @@ bool SpatialPooler::operator==(const SpatialPooler& o) const{
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;
Expand Down
72 changes: 30 additions & 42 deletions src/htm/algorithms/SpatialPooler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,18 +230,21 @@ class SpatialPooler : public Serializable
will be removed from activeVector. TODO: we may want to keep
boosting on even when learning is off.

@param active An SDR representing the winning columns after
inhibition. The size of the SDR is equal to the number of
columns (also returned by the method getNumColumns).

@return overlap
an int vector containing the overlap score for each column. The
overlap score for a column is defined as the number of synapses in
a "connected state" (connected synapses) that are connected to
input bits which are turned on.
Replaces: SP.calculateOverlaps_(), SP.getOverlaps()
@return param `active` is filled with active output columns.

@return overlaps dense vector of number of connected synapses for each column `active`.
Determines each column's overlap with the current input vector.
The overlap of a column is the number of synapses for that column
that are connected (permanence value is greater than
'synPermConnected') to input bits which are turned on.
The overlap score for a column is defined as the number of synapses in
a "connected state" (connected synapses) that are connected to
input bits which are turned on.
This replaces deprecated method `SP.getOverlaps()`
*/
virtual const vector<SynapseIdx> compute(const SDR &input, const bool learn, SDR &active);
virtual std::vector<SynapseIdx> compute(const SDR &input,
const bool learn,
SDR &active) const;


/**
Expand Down Expand Up @@ -467,33 +470,6 @@ class SpatialPooler : public Serializable
*/
void setBoostStrength(Real boostStrength);

/**
Returns the iteration number.

@returns integer number of iteration number.
*/
UInt getIterationNum() const;

/**
Sets the iteration number.

@param iterationNum integer number of iteration number.
*/
void setIterationNum(UInt iterationNum);

/**
Returns the learning iteration number.

@returns integer of the learning iteration number.
*/
UInt getIterationLearnNum() const;

/**
Sets the learning iteration number.

@param iterationLearnNum integer of learning iteration number.
*/
void setIterationLearnNum(UInt iterationLearnNum);

/**
Returns the verbosity level.
Expand Down Expand Up @@ -744,18 +720,26 @@ class SpatialPooler : public Serializable


/**
Returns the boosted overlap score for each column.
* Apply boosting (see param @ref `boostStrength` in constructor) to the overlap.
*
* @param overlaps vector returns from @ref `compute()`. Signifies overlap of the SP with
* the input, aka. activation.
*
* @deprecate replaces deprecated `SP.getBoostedOverlaps_()`; now use
* `auto over = compute(..); auto boosted = sp.getBoostedOverlaps(over);`
* Also replaces `SP.boostOverlaps_()`
*
* @return boosted overlaps for each input.
*
*/
const vector<Real> &getBoostedOverlaps() const;
const vector<Real>& getBoostedOverlaps(const vector<SynapseIdx> overlaps) const;

///////////////////////////////////////////////////////////
//
// Implementation methods. all methods below this line are
// NOT part of the public API


void boostOverlaps_(const vector<SynapseIdx> &overlaps, vector<Real> &boostedOverlaps) const;

/**
Maps a column to its respective input index, keeping to the topology of
the region. It takes the index of the column as an argument and determines
Expand Down Expand Up @@ -853,6 +837,10 @@ class SpatialPooler : public Serializable

void clip_(vector<Real> &perm) const;

void raisePermanencesToThreshold_(vector<Real> &perm,
const vector<UInt> &potential) const;


/**
Performs inhibition. This method calculates the necessary values needed to
actually perform inhibition and then delegates the task of picking the
Expand Down
30 changes: 0 additions & 30 deletions src/htm/utils/Random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,37 +57,7 @@ Random::Random(UInt64 seed) {
steps_ = 0;
}


namespace htm {
std::ostream &operator<<(std::ostream &outStream, const Random &r) {
outStream << "random-v2" << " ";
outStream << r.seed_ << " ";
outStream << r.steps_ << " ";
outStream << "endrandom-v2" << " ";
return outStream;
}


std::istream &operator>>(std::istream &inStream, Random &r) {
std::string version;

inStream >> version;
NTA_CHECK(version == "random-v2") << "Random() deserializer -- found unexpected version string '"
<< version << "'";
inStream >> r.seed_;
r.gen.seed(static_cast<unsigned int>(r.seed_)); //reseed
inStream >> r.steps_;
r.gen.discard(r.steps_); //advance n steps
//FIXME we could de/serialize directly RNG gen, it should be multi-platform according to standard,
//but on OSX CI it wasn't (25/11/2018). So "hacking" the above instead.
std::string endtag;
inStream >> endtag;
NTA_CHECK(endtag == "endrandom-v2") << "Random() deserializer -- found unexpected end tag '" << endtag << "'";
inStream.ignore(1);

return inStream;
}

// helper function for seeding RNGs across the plugin barrier
UInt32 GetRandomSeed() {
return htm::Random().getUInt32();
Expand Down
Loading