Skip to content
2 changes: 0 additions & 2 deletions bindings/py/cpp_src/bindings/algorithms/py_Connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ R"(Returns pair of:

py_Connections.def("bumpSegment", &Connections::bumpSegment);

py_Connections.def("destroyMinPermanenceSynapses", &Connections::destroyMinPermanenceSynapses);

py_Connections.def("numCells", &Connections::numCells);

py_Connections.def("numSegments",
Expand Down
35 changes: 0 additions & 35 deletions src/htm/algorithms/Connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,41 +594,6 @@ void Connections::bumpSegment(const Segment segment, const Permanence delta) {
}


void Connections::destroyMinPermanenceSynapses(
const Segment segment, Int nDestroy,
const vector<CellIdx> &excludeCells)
{
NTA_ASSERT( nDestroy >= 0 );

// 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 htm {
/**
* print statistics in human readable form
Expand Down
11 changes: 0 additions & 11 deletions src/htm/algorithms/Connections.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,17 +525,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,
Copy link
Member Author

Choose a reason for hiding this comment

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

removed as not entirely bio-inspired, using synaptic decay instead.

PS: I also hope for some better cross platform determinism, as the simpler code does not depend so much on ordering of synapses.

Copy link
Member Author

Choose a reason for hiding this comment

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

removed as not entirely bio-inspired, using synaptic decay instead.

I now know what you're upto, and what was actually missing from this PR! the decay itself.
I'm gonna implement it for the synapses' updates.

const SDR_sparse_t &excludeCells = {});

/**
* Print diagnostic info
Expand Down
13 changes: 2 additions & 11 deletions src/htm/algorithms/TemporalMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,22 +189,13 @@ void TemporalMemory::growSynapses_(
vector<CellIdx> candidates(prevWinnerCells.begin(), prevWinnerCells.end());
NTA_ASSERT(std::is_sorted(candidates.begin(), candidates.end()));

//figure the number of new synapses to grow
const size_t nActual = std::min(static_cast<size_t>(nDesiredNewSynapses), candidates.size());
// ..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); //TODO move this functionality to Connections.createSynapse()
}
// ..Recalculate in case we weren't able to destroy as many synapses as needed.
const size_t nActualWithMax = std::min(nActual, static_cast<size_t>(maxSynapsesPerSegment_) - connections.numSynapses(segment));
const size_t nActual = std::min(static_cast<size_t>(nDesiredNewSynapses) + (size_t)connections.numSynapses(segment), (size_t)maxSynapsesPerSegment_); //even with the new additions, synapses fit to segment's limit

// Pick nActual cells randomly.
rng_.shuffle(candidates.begin(), candidates.end());
const size_t nDesired = connections.numSynapses(segment) + nActualWithMax; //num synapses on seg after this function (+-), see #COND
for (const auto syn : candidates) {
// #COND: this loop finishes two folds: a) we ran out of candidates (above), b) we grew the desired number of new synapses (below)
if(connections.numSynapses(segment) == nDesired) break;
if(connections.numSynapses(segment) == nActual) break;
connections.createSynapse(segment, syn, initialPermanence_); //TODO createSynapse consider creating a vector of new synapses at once?
}
}
Expand Down
29 changes: 16 additions & 13 deletions src/test/unit/algorithms/TemporalMemoryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,8 +794,8 @@ TEST(TemporalMemoryTest, RecycleWeakestSynapseToMakeRoomForNewSynapse) {
/*connectedPermanence*/ 0.50f,
/*minThreshold*/ 1,
/*maxNewSynapseCount*/ 3,
/*permanenceIncrement*/ 0.02f,
/*permanenceDecrement*/ 0.02f,
/*permanenceIncrement*/ 0.06f,
/*permanenceDecrement*/ 0.05f,
/*predictedSegmentDecrement*/ 0.0f,
/*seed*/ 42,
/*maxSegmentsPerCell*/ 255,
Expand All @@ -810,33 +810,36 @@ TEST(TemporalMemoryTest, RecycleWeakestSynapseToMakeRoomForNewSynapse) {

Segment matchingSegment = tm.createSegment(4);

// Create a weak synapse. Make sure it's not so weak that
// permanenceDecrement destroys it.
// A: Create a weak synapse. This synapse should be destroyed in the experiment.
// Make sure it's not so weak that permanenceDecrement destroys it on a single
// step. (0.11 < 3x0.05 => in 3 steps it should be gone)
tm.connections.createSynapse(matchingSegment, 0, 0.11f);

// Create a synapse that will match.
// B: Create a synapse that will match. ('1' is in the active columns, 0 above is not)
tm.connections.createSynapse(matchingSegment, 1, 0.20f);

// Create a synapse with a high permanence.
// C: Create a synapse with a high permanence. (31 is also not in the active cols,
// but here permanence is so high that it would not be removed)
tm.connections.createSynapse(matchingSegment, 31, 0.6f);

// Activate a synapse on the segment, making it "matching".
for(int i=0; i< 3; i++) {
// Activate a synapse on the segment, making it "matching". (B matches, as it's presyn cell '1'
// is in active cols).
tm.compute(previousActiveColumns);

ASSERT_EQ(prevWinnerCells, tm.getWinnerCells());

// Now mark the segment as "correct" by activating its cell.
tm.compute(activeColumns);
}

// There should now be 3 synapses, and none of them should be to cell 0.
// There should now be 3 synapses, and none of them should be to cell '0' (A).
const vector<Synapse> &synapses =
tm.connections.synapsesForSegment(matchingSegment);
ASSERT_EQ(4ul, synapses.size());

std::set<CellIdx> presynapticCells;
for (Synapse synapse : synapses) {
presynapticCells.insert(
tm.connections.dataForSynapse(synapse).presynapticCell);
const auto presyn = tm.connections.dataForSynapse(synapse).presynapticCell;
EXPECT_TRUE(presyn != 0) << "Permanence to cell '0' in case A should have been deleted.";
presynapticCells.insert(presyn);
}

std::set<CellIdx> expected = {1, 2, 3, 31};
Expand Down