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

Connections: replace destroyMinPermSynapses() with plausible decay #751

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
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()
breznak marked this conversation as resolved.
Show resolved Hide resolved
}
// ..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.";
breznak marked this conversation as resolved.
Show resolved Hide resolved
presynapticCells.insert(presyn);
}

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