Skip to content

Commit

Permalink
Connections: Remove unused method & arguments
Browse files Browse the repository at this point in the history
Methods:
 - Connections::computeActivity, and
 - Connections::raisePermanencesToThreshold
no longer accept a synapse permanence threshold argument.  The connections
instance must be initialized with the correct threshold, and once initialized
it can not be changed.  Previously these methods would break if given the
wrong threshold.
  • Loading branch information
ctrl-z-9000-times committed Apr 4, 2019
1 parent f84b583 commit bbf2e64
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 87 deletions.
4 changes: 4 additions & 0 deletions API_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ are ignored. PR #271
* Changed SDRClassifier::compute() signature to take parameter `ClassifierResult& result`, instead of a raw pointer. PR #301

* Rewrote ScalarEncoder API, all code using it needs to be rewritten. PR #314

* Connections class must be initialized with a connectedPermanence. Methods
`Connections::computeActivity` and `Connections::raisePermanencesToThreshold` no
longer accept a synapse permanence threshold argument.
61 changes: 25 additions & 36 deletions src/nupic/algorithms/Connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,17 +383,6 @@ Connections::synapsesForPresynapticCell(CellIdx presynapticCell) const {
return all;
}

void Connections::computeActivity(
vector<UInt32> &numActiveConnectedSynapsesForSegment,
vector<UInt32> &numActivePotentialSynapsesForSegment,
CellIdx activePresynapticCell, Permanence connectedPermanence) const {
std::vector<UInt32> activePresynapticCells({activePresynapticCell});
computeActivity(
numActiveConnectedSynapsesForSegment,
numActivePotentialSynapsesForSegment,
activePresynapticCells, connectedPermanence);
}


void Connections::computeActivity(
vector<UInt32> &numActiveConnectedSynapsesForSegment,
Expand All @@ -414,11 +403,9 @@ void Connections::computeActivity(
void Connections::computeActivity(
vector<UInt32> &numActiveConnectedSynapsesForSegment,
vector<UInt32> &numActivePotentialSynapsesForSegment,
const vector<CellIdx> &activePresynapticCells,
Permanence connectedPermanence) const {
const vector<CellIdx> &activePresynapticCells) const {
NTA_ASSERT(numActiveConnectedSynapsesForSegment.size() == segments_.size());
NTA_ASSERT(numActivePotentialSynapsesForSegment.size() == segments_.size());
NTA_CHECK( abs(connectedPermanence - nupic::Epsilon - connectedThreshold_) <= nupic::Epsilon );

// Iterate through all connected synapses.
computeActivity(
Expand Down Expand Up @@ -462,53 +449,55 @@ void Connections::adaptSegment(const Segment segment,
}
}

/** called for under-performing Segments. (can have synapses pruned, etc.)
* After the call, Segment will have at least
* segmentThreshold synapses connected (>= permanenceThreshold).
* So the Segment could likely be active next time.
/**
* Called for under-performing Segments (can have synapses pruned, etc.). After
* the call, Segment will have at least segmentThreshold synapses connected, so
* the Segment could be active next time.
*/
void Connections::raisePermanencesToThreshold(
const Segment segment,
const Permanence permanenceThreshold,
const UInt segmentThreshold)
{
if( segmentThreshold == 0 ) //no synapses requested to be connected, done.
if( segmentThreshold == 0 ) // No synapses requested to be connected, done.
return;

NTA_ASSERT(segment < segments_.size()) << "Accessing segment out of bounds.";
auto &segData = segments_[segment];
if( segData.numConnected >= segmentThreshold ) //the segment already satisfies the requirement, done.
return;
if( segData.numConnected >= segmentThreshold )
return; // The segment already satisfies the requirement, done.

vector<Synapse> &synapses = segData.synapses;
if( synapses.empty()) return; //no synapses to raise permanences to, no work
if( synapses.empty())
return; // No synapses to raise permanences to, no work to do.

// Prune empty segment? No.
// The SP calls this method, but the SP does not do any pruning.
// The TM already has code to do pruning, but it doesn't ever call this method.

// There can be situation when synapses are pruned so the segment has too few synapses to ever activate.
// (so we cannot satisfy the >= segmentThreshold connected).
// In this case the method should do the next best thing and connect as many synapses as it can.
//
//keep segmentThreshold within synapses range
const auto threshold = std::min((size_t)segmentThreshold, synapses.size());
// There can be situations when synapses are pruned so the segment has too few
// synapses to ever activate, so we cannot satisfy the >= segmentThreshold
// connected. In this case the method should do the next best thing and
// connect as many synapses as it can.

// Keep segmentThreshold within synapses range.
const auto threshold = std::min((size_t)segmentThreshold, synapses.size());

// Sort the potential pool by permanence values, and look for the synapse with
// the N'th greatest permanence, where N is the desired minimum number of
// connected synapses. Then calculate how much to increase the N'th synapses
// permance by such that it becomes a connected synapse.
// After that there will be at least N synapses connected.
// permance by such that it becomes a connected synapse. After that there
// will be at least N synapses connected.

// Threshold is ensured to be >=1 by condition at very beginning if(thresh == 0)...
auto minPermSynPtr = synapses.begin() + threshold - 1;

auto minPermSynPtr = synapses.begin() + threshold - 1; //threshold is ensured to be >=1 by condition at very beginning if(thresh == 0)...
// Do a partial sort, it's faster than a full sort. Only minPermSynPtr is in
// its final sorted position.
const auto permanencesGreater = [&](const Synapse &A, const Synapse &B)
{ return synapses_[A].permanence > synapses_[B].permanence; };
// Do a partial sort, it's faster than a full sort.
std::nth_element(synapses.begin(), minPermSynPtr, synapses.end(), permanencesGreater);

const Real increment = permanenceThreshold - synapses_[ *minPermSynPtr ].permanence;
if( increment <= 0 ) // if( minPermSynPtr is already connected ) then ...
const Real increment = connectedThreshold_ - synapses_[ *minPermSynPtr ].permanence;
if( increment <= 0 ) // If minPermSynPtr is already connected then ...
return; // Enough synapses are already connected.

// Raise the permance of all synapses in the potential pool uniformly.
Expand Down
37 changes: 3 additions & 34 deletions src/nupic/algorithms/Connections.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,44 +373,15 @@ class Connections : public Serializable
*
* @param activePresynapticCells
* Active cells in the input.
*
* @param connectedPermanence
* Minimum permanence for a synapse to be "connected".
*/
void
computeActivity(std::vector<UInt32> &numActiveConnectedSynapsesForSegment,
std::vector<UInt32> &numActivePotentialSynapsesForSegment,
const std::vector<CellIdx> &activePresynapticCells,
Permanence connectedPermanence) const;

void
computeActivity(std::vector<UInt32> &numActiveConnectedSynapsesForSegment,
const std::vector<CellIdx> &activePresynapticCells) const;

/**
* Compute the segment excitations for a single active presynaptic cell.
*
* The output vectors aren't grown or cleared. They must be
* preinitialized with the length returned by
* getSegmentFlatVectorLength().
*
* @param numActiveConnectedSynapsesForSegment
* An output vector for active connected synapse counts per segment.
*
* @param numActivePotentialSynapsesForSegment
* An output vector for active potential synapse counts per segment.
*
* @param activePresynapticCells
* Active cells in the input.
*
* @param connectedPermanence
* Minimum permanence for a synapse to be "connected".
*/
void
computeActivity(std::vector<UInt32> &numActiveConnectedSynapsesForSegment,
std::vector<UInt32> &numActivePotentialSynapsesForSegment,
CellIdx activePresynapticCell,
Permanence connectedPermanence) const;
const std::vector<CellIdx> &activePresynapticCells) const;

/**
* The primary method in charge of learning. Adapts the permanence values of
Expand All @@ -433,14 +404,12 @@ class Connections : public Serializable
/**
* Ensures a minimum number of connected synapses. This raises permance
* values until the desired number of synapses have permanences above the
* permanenceThreshold. This is applied to a single segment.
* connectedThreshold. This is applied to a single segment.
*
* @param segment Index of segment on cell. Is returned by method getSegment.
* @param permanenceThreshold Connected threshold of synapses
* @param segmentThreshold Desired number of connected synapses
* @param segmentThreshold Desired number of connected synapses.
*/
void raisePermanencesToThreshold(const Segment segment,
const Permanence permanenceThreshold,
const UInt segmentThreshold);

/**
Expand Down
5 changes: 2 additions & 3 deletions src/nupic/algorithms/SpatialPooler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ void SpatialPooler::initialize(
connections_.createSynapse( (connections::Segment)i, presyn, perm[presyn] );
}

connections_.raisePermanencesToThreshold( (connections::Segment)i, synPermConnected_, stimulusThreshold_ );
connections_.raisePermanencesToThreshold( (connections::Segment)i, stimulusThreshold_ );
}

updateInhibitionRadius_();
Expand Down Expand Up @@ -739,8 +739,7 @@ void SpatialPooler::adaptSynapses_(const SDR &input,
const SDR &active) {
for(const auto &column : active.getSparse()) {
connections_.adaptSegment(column, input, synPermActiveInc_, synPermInactiveDec_);
connections_.raisePermanencesToThreshold(
column, synPermConnected_, stimulusThreshold_);
connections_.raisePermanencesToThreshold( column, stimulusThreshold_ );
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/nupic/algorithms/TemporalMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ void TemporalMemory::activateDendrites(bool learn,
numActivePotentialSynapsesForSegment_.assign(length, 0);
connections.computeActivity(numActiveConnectedSynapsesForSegment_,
numActivePotentialSynapsesForSegment_,
activeCells_, connectedPermanence_);
activeCells_);

// Active segments, connected synapses.
activeSegments_.clear();
Expand Down
25 changes: 12 additions & 13 deletions src/test/unit/algorithms/ConnectionsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ void computeSampleActivity(Connections &connections) {
vector<UInt32> numActivePotentialSynapsesForSegment(
connections.segmentFlatListLength(), 0);
connections.computeActivity(numActiveConnectedSynapsesForSegment,
numActivePotentialSynapsesForSegment, input, 0.5f);
numActivePotentialSynapsesForSegment, input);
}

/**
Expand Down Expand Up @@ -167,7 +167,7 @@ TEST(ConnectionsTest, testDestroySegment) {
connections.segmentFlatListLength(), 0);
connections.computeActivity(numActiveConnectedSynapsesForSegment,
numActivePotentialSynapsesForSegment,
{80, 81, 82}, 0.5f);
{80, 81, 82});

ASSERT_EQ(0ul, numActiveConnectedSynapsesForSegment[segment2]);
ASSERT_EQ(0ul, numActivePotentialSynapsesForSegment[segment2]);
Expand Down Expand Up @@ -198,7 +198,7 @@ TEST(ConnectionsTest, testDestroySynapse) {
connections.segmentFlatListLength(), 0);
connections.computeActivity(numActiveConnectedSynapsesForSegment,
numActivePotentialSynapsesForSegment,
{80, 81, 82}, 0.5f);
{80, 81, 82});

ASSERT_EQ(1ul, numActiveConnectedSynapsesForSegment[segment]);
ASSERT_EQ(2ul, numActivePotentialSynapsesForSegment[segment]);
Expand Down Expand Up @@ -355,7 +355,7 @@ TEST(ConnectionsTest, testComputeActivity) {
vector<UInt32> numActivePotentialSynapsesForSegment(
connections.segmentFlatListLength(), 0);
connections.computeActivity(numActiveConnectedSynapsesForSegment,
numActivePotentialSynapsesForSegment, input, 0.5f);
numActivePotentialSynapsesForSegment, input);

ASSERT_EQ(1ul, numActiveConnectedSynapsesForSegment[segment1_1]);
ASSERT_EQ(2ul, numActivePotentialSynapsesForSegment[segment1_1]);
Expand Down Expand Up @@ -422,7 +422,6 @@ TEST(ConnectionsTest, testAdaptSynapses) {
TEST(ConnectionsTest, testRaisePermanencesToThreshold) {
UInt stimulusThreshold = 3;
Real synPermConnected = 0.1f;
Real synPermBelowStimulusInc = 0.01f;
UInt numInputs = 5;
UInt numCells = 7;
Connections con(numCells, synPermConnected);
Expand Down Expand Up @@ -457,26 +456,26 @@ TEST(ConnectionsTest, testRaisePermanencesToThreshold) {
}
}
// Run method under test.
con.raisePermanencesToThreshold(i, synPermConnected, stimulusThreshold);
con.raisePermanencesToThreshold(i, stimulusThreshold);
// Check results.
for(auto syn : con.synapsesForSegment(i)) {
auto synData = con.dataForSynapse( syn );
UInt presyn = synData.presynapticCell;
ASSERT_NEAR(truePerm[i][presyn], synData.permanence,
synPermBelowStimulusInc);
ASSERT_NEAR(truePerm[i][presyn], synData.permanence, 0.01f);
}
}
}


TEST(ConnectionsTest, testRaisePermanencesToThresholdOutOfBounds) {
Connections con(1001, 0.21f);
Connections con(1001, 0.666f);

// check empty segment (with no synapse data)
auto emptySegment = con.createSegment(0);
auto synapses = con.synapsesForSegment(emptySegment);
NTA_CHECK(synapses.empty()) << "We want to create a Segment with none synapses";
EXPECT_NO_THROW( con.raisePermanencesToThreshold(emptySegment, (Permanence)0.1337, 3u) ) << "raisePermanence fails when empty Segment encountered";
NTA_CHECK(synapses.empty()) << "We want to create a Segment with no synapses";
EXPECT_NO_THROW( con.raisePermanencesToThreshold(emptySegment, 3u) )
<< "raisePermanence fails when empty Segment encountered";

// check segment with 3 synapses, but wanted to raise 5
auto segWith3Syn = con.createSegment(0);
Expand All @@ -485,8 +484,8 @@ TEST(ConnectionsTest, testRaisePermanencesToThresholdOutOfBounds) {
con.createSynapse( segWith3Syn, 18, 0.25f);
con.createSynapse( segWith3Syn, 121, 0.00001f);
NTA_CHECK(con.synapsesForSegment(segWith3Syn).size() == 3) << "We failed to create 3 synapses on a segment";
EXPECT_NO_THROW( con.raisePermanencesToThreshold(segWith3Syn, (Permanence)0.666, 5u) ) << "raisePermanence fails when lower number of available synapses than requested by threshold";

EXPECT_NO_THROW( con.raisePermanencesToThreshold(segWith3Syn, 5u) )
<< "raisePermanence fails when lower number of available synapses than requested by threshold";
}

TEST(ConnectionsTest, testBumpSegment) {
Expand Down

0 comments on commit bbf2e64

Please sign in to comment.