All Downloads are FREE. Search and download functionalities are using the official Maven repository.

model.MARK_II.generalAlgorithm.TemporalPooler Maven / Gradle / Ivy

The newest version!
package model.MARK_II.generalAlgorithm;

import com.google.gson.Gson;
import model.MARK_II.region.*;
import model.MARK_II.util.FileInputOutput;

import java.io.IOException;
import java.util.*;

/**
 * Idea behind temporal pooling: SDRs that occur adjacent in time probably have
 * a common underlying cause. Several times a second your eyes fixate on a
 * different part of the image causing a complete change in input. Despite this
 * changing input your perception is stable. Somewhere in higher region there
 * must be neurons that remain active.
 *
 * Input into TemporalPooler: activeColumns of a Region at time t computed by
 * SpatialPooler
 *
 * Output from TemporalPooler: boolean OR of the current active and predictive
 * state for each neuron in the set of activeColumns of a Region.
 *
 * @author Quinn Liu ([email protected])
 * @version April 27, 2014
 */
public class TemporalPooler extends Pooler {
    private SpatialPooler spatialPooler;

    private final int newSynapseCount;
    private List currentLearningNeurons;
    private SegmentUpdateList segmentUpdateList;
    private Set predictiveColumnsAtTForTPlus1;
    private Set predictiveColumnsAtTMinus1;

    public TemporalPooler(SpatialPooler spatialPooler, int newSynapseCount) {
        this.spatialPooler = spatialPooler;
        super.region = spatialPooler.getRegion();
        this.segmentUpdateList = new SegmentUpdateList();

        this.newSynapseCount = newSynapseCount;

        this.currentLearningNeurons = new ArrayList();
        this.predictiveColumnsAtTForTPlus1 = new HashSet();
        this.predictiveColumnsAtTMinus1 = new HashSet();
    }

    public void performPooling() {
        Set activeColumns = this.spatialPooler.getActiveColumns();
        if (super.getLearningState()) {
            this.phaseOne(activeColumns);
            this.phaseTwo();
            this.phaseThree();
        } else {
            this.computeActiveStateOfAllNeuronsInActiveColumn(activeColumns);
            this.computePredictiveStateOfAllNeurons();
        }
    }

    public SpatialPooler getSpatialPooler() {
        return this.spatialPooler;
    }

    public void nextTimeStep() {
        Column[][] columns = super.region.getColumns();
        for (int row = 0; row < super.region.getNumberOfRowsAlongRegionYAxis(); row++) {
            for (int column = 0; column < super.region.getNumberOfColumnsAlongRegionXAxis(); column++) {
                for (Neuron neuron : columns[row][column].getNeurons()) {
                    neuron.nextTimeStep();

                    for (DistalSegment distalSegment : neuron
                            .getDistalSegments()) {
                        distalSegment.nextTimeStep();
                    }
                }
            }
        }
        this.spatialPooler.getAlgorithmStatistics().nextTimeStep();
        this.currentLearningNeurons.clear();
        this.segmentUpdateList.clear();
        this.predictiveColumnsAtTMinus1.addAll(this.predictiveColumnsAtTForTPlus1);
        this.predictiveColumnsAtTForTPlus1.clear();
    }

    /**
     * Compute the activeState for each Neuron in activeColumns. Then in each
     * active Column a learning Neuron is chosen.
     */
    void phaseOne(Set activeColumns) {
        /// for c in activeColumns(t)
        for (Column column : activeColumns) {
            /// buPredicted = false
            boolean bottomUpPredicted = false;
            /// lcChosen = false
            boolean learningCellChosen = false;

            Neuron[] neurons = column.getNeurons();
            /// for i = 0 to cellsPerColumn - 1
            for (int i = 0; i < neurons.length; i++) {
                /// predictiveState(c, i, t-1) == true then
                if (neurons[i].getPreviousActiveState() == true) {
                    /// s = getActiveSegment(c, i, t-1, activeState)
                    DistalSegment bestSegment = neurons[i]
                            .getBestPreviousActiveSegment(this.spatialPooler.getAlgorithmStatistics());

                    /// if s.sequenceSegment == true then
                    if (bestSegment != null
                            && bestSegment
                            .getSequenceStatePredictsFeedFowardInputOnNextStep()) {
                        /// buPredicted = true
                        bottomUpPredicted = true;
                        /// activeState(c, i, t) = 1
                        neurons[i].setActiveState(true);

                        /// if segmentActive(s, t-1, learnState) then
                        if (bestSegment.getPreviousActiveState()) {
                            /// lcChosen = true
                            learningCellChosen = true;
                            /// learnState(c, i, t) = 1
                            column.setLearningNeuronPosition(i);
                            this.currentLearningNeurons.add(neurons[i]);
                        }
                    }
                }
            }
            /// if buPredicted == false then
            if (bottomUpPredicted == false) {
                /// for i = 0 to cellsPerColumn - 1
                for (Neuron neuron : column.getNeurons()) {
                    /// activeState(c, i, t) = 1
                    neuron.setActiveState(true);
                }
            }

            /// if lcChosen == false then
            if (learningCellChosen == false) {
                /// l,s = getBestMatchingCell(c, t-1)
                int bestNeuronIndex = this.getBestMatchingNeuronIndex(column);
                /// learnState(c, i, t) = 1
                column.setLearningNeuronPosition(bestNeuronIndex);
                this.currentLearningNeurons.add(column
                        .getNeuron(bestNeuronIndex));

                DistalSegment segment = neurons[bestNeuronIndex]
                        .getBestPreviousActiveSegment(this.spatialPooler.getAlgorithmStatistics());
                /// sUpdate = getSegmentActiveSynapses(c, i, s, t-1, true)
                SegmentUpdate segmentUpdate = this.getSegmentActiveSynapses(
                        column.getCurrentPosition(), bestNeuronIndex, segment,
                        true, true);
                /// sUpdate.sequenceSegment = true
                segmentUpdate.setSequenceState(true);
                segment.setSequenceState(true);
                this.spatialPooler.getAlgorithmStatistics().getTP_sequenceSegmentsHistoryAndAdd(1);

                /// segmentUpdateList.add(sUpdate)
                this.segmentUpdateList.add(segmentUpdate);
            }
        }
        this.spatialPooler.getAlgorithmStatistics().getTP_learningNeuronsHistoryAndAdd(this.currentLearningNeurons.size());
    }

    /**
     * @param newSynapses Actually adding new Synapses to given segment object
     * @return A segmentUpdate data structure containing a list of proposed
     * changes to segment. Let activeSynapses be the list of active
     * synapses where the originating cells have their activeState
     * output = 1 at time step t. (This list is empty if s = -1 since
     * the segment doesn't exist.) newSynapses is an optional argument
     * that defaults to false. If newSynapses is true, then
     * newSynapseCount - count(activeSynapses) synapses are added to
     * activeSynapses. These synapses are randomly chosen from the set
     * of cells that have learnState output = 1 at time step t.
     */
    SegmentUpdate getSegmentActiveSynapses(ColumnPosition columnPosition,
                                           int neuronIndex, Segment segment, boolean previousTimeStep,
                                           boolean newSynapses) {
        Set> activeSynapses = new HashSet>();
        Set> deactiveSynapses = new HashSet>();

        for (Synapse synapse : segment.getSynapses()) {

            if (previousTimeStep) {
                if (synapse.getCell().getPreviousActiveState()) {
                    activeSynapses.add(synapse);
                } else {
                    deactiveSynapses.add(synapse);
                }
            } else {
                if (synapse.getCell().getActiveState()) {
                    activeSynapses.add(synapse);
                } else {
                    deactiveSynapses.add(synapse);
                }
            }
        }

        if (newSynapses) {
            activeSynapses = this
                    .addRandomlyChosenSynapsesFromCurrentLearningNeurons(
                            activeSynapses, segment, columnPosition);
        }

        return new SegmentUpdate(activeSynapses, deactiveSynapses,
                columnPosition, neuronIndex);
    }

    Set> addRandomlyChosenSynapsesFromCurrentLearningNeurons(
            Set> activeSynapses, Segment segment,
            ColumnPosition columnPosition) {
        if (this.currentLearningNeurons.size() == 0) {
            throw new IllegalStateException(
                    "currentLearningNeurons in TemporalPooler class "
                            + "addRandomlyChosenSynapsesFromCurrentLearningNeurons"
                            + " method cannot be size 0");
        }

        int numberOfSynapsesToAdd = this.newSynapseCount
                - activeSynapses.size();

        List> potentialSynapsesToAdd = this
                .generatePotentialSynapses(numberOfSynapsesToAdd,
                        columnPosition);

        for (int i = 0; i < numberOfSynapsesToAdd; i++) {
            activeSynapses.add(potentialSynapsesToAdd.get(i));
            segment.addSynapse(potentialSynapsesToAdd.get(i));
        }

        return activeSynapses;
    }

    /**
     * This method must never return an emtpy list.
     */
    List> generatePotentialSynapses(int numberOfSynapsesToAdd,
                                                  ColumnPosition columnPosition) {
        List> potentialSynapsesToAdd = new ArrayList>();
        for (Neuron neuron : this.currentLearningNeurons) {
            // it is okay if initally no learning neurons have any distal
            // segments
            for (DistalSegment distalSegment : neuron.getDistalSegments()) {
                if (potentialSynapsesToAdd.size() >= numberOfSynapsesToAdd) {
                    break;
                } else {
                    potentialSynapsesToAdd.addAll(distalSegment.getSynapses());
                }
            }
            // it is possible potentialSynapsesToAdd.size() is still <
            // numberOfSynapsesToAdd
            if (potentialSynapsesToAdd.size() >= numberOfSynapsesToAdd) {
                break;
            }
        }

        // it is possible potentialSynapsesToAdd.size() is still <
        // numberOfSynapsesToAdd and this is a problem if it is empty
        // because then a neuron's segments will never have any new Synapses
        if (numberOfSynapsesToAdd > potentialSynapsesToAdd.size()) {
            potentialSynapsesToAdd = this
                    .createNewSynapsesConnectedToCurrentLearningNeurons(
                            potentialSynapsesToAdd, numberOfSynapsesToAdd,
                            columnPosition);
        }
        return potentialSynapsesToAdd;
    }

    List getCurrentLearningNeurons() {
        return this.currentLearningNeurons;
    }

    List> createNewSynapsesConnectedToCurrentLearningNeurons(
            List> potentialSynapsesToAdd,
            int numberOfSynapsesToAdd, ColumnPosition columnPosition) {

        int remainingNumberOfSynapsesToAdd = numberOfSynapsesToAdd
                - potentialSynapsesToAdd.size();

        this.spatialPooler.getAlgorithmStatistics().getTP_synapsesHistoryAndAdd(remainingNumberOfSynapsesToAdd);

        int numberOfLearningNeurons = this.currentLearningNeurons.size();
        if (numberOfLearningNeurons == 0) {
            throw new IllegalStateException(
                    "currentLearningNeurons in TemporalPooler class "
                            + "createNewSynapsesConnectedToCurrentLearningNeurons"
                            + " method cannot be size 0");
        }

        int learningNeuronIndex = 0;
        for (int i = 0; i < remainingNumberOfSynapsesToAdd; i++) {
            Synapse newSynapse = new Synapse(
                    this.currentLearningNeurons.get(learningNeuronIndex),
                    columnPosition.getRow(), columnPosition.getColumn());
            potentialSynapsesToAdd.add(newSynapse);

            if ((learningNeuronIndex + 1) < numberOfLearningNeurons) {
                learningNeuronIndex++;
            } else { // wrap around and so as many different learning neurons
                // are used
                learningNeuronIndex = 0;
            }
        }
        return potentialSynapsesToAdd;
    }

    /**
     * Calculated the predictive state for each Neuron. A Neuron's
     * predictiveState will be true if 1 or more distal segments becomes active.
     */
    void phaseTwo() {
        /// for c, i in cells
        Column[][] columns = this.region.getColumns();
        for (int rowIndex = 0; rowIndex < columns.length; rowIndex++) {
            for (int columnIndex = 0; columnIndex < columns[0].length; columnIndex++) {
                Column column = columns[rowIndex][columnIndex];
                Neuron[] neurons = column.getNeurons();
                for (int i = 0; i < neurons.length; i++) {
                    // we must compute the best segment here because
                    // if we compute it where it is commented out below
                    // then we would be iterating over the neuron's list
                    // of segments again
                    Segment predictingSegment = neurons[i]
                            .getBestPreviousActiveSegment(this.spatialPooler
                                    .getAlgorithmStatistics());

                    /// for s in segments(c, i)
                    for (Segment segment : neurons[i].getDistalSegments()) {
                        // NOTE: segment may become active during the spatial pooling
                        // between temporal pooling iterations
                        /// if segmentActive(s, t, activeState) then
                        if (segment.getActiveState()) {
                            /// predictiveState(c, i, t) = 1
                            neurons[i].setPredictingState(true);
                            this.spatialPooler.getAlgorithmStatistics().getTP_activeDistalSegmentsHistoryAndAdd(1);
                            this.predictiveColumnsAtTForTPlus1.add(column
                                    .getCurrentPosition());

                            /// activeUpdate = getSegmentActiveSynapses(c, i, s, t, false)
                            SegmentUpdate activeUpdate = this
                                    .getSegmentActiveSynapses(
                                            column.getCurrentPosition(), i,
                                            segment, false, false);
                            /// segmentUpdateList.add(activeUpdate)
                            this.segmentUpdateList.add(activeUpdate);
                            // Segment predictingSegment = neurons[i]
                            // .getBestPreviousActiveSegment();

                            /// predSegment = getBestMatchingSegment(c, i, t-1)
                            /// predUpdate = getSegmentActiveSynapses(c, i, predSegment, t-1, true)
                            SegmentUpdate predictionUpdate = this
                                    .getSegmentActiveSynapses(
                                            column.getCurrentPosition(), i,
                                            predictingSegment, true, true);
                            /// segmentUpdateList.add(predUpdate)
                            this.segmentUpdateList.add(predictionUpdate);
                        }
                    }
                }
            }
        }
        this.spatialPooler.getAlgorithmStatistics()
                .getTP_predictionScoreHistoryAndAdd(super.algorithmStatistics
                        .computePredictionScore(this.spatialPooler.getActiveColumnPositions(), this.predictiveColumnsAtTForTPlus1));
    }

    /**
     * Carries out learning. Segment updates that have been queued up are
     * actually implemented once we get feed-forward input and a Neuron is
     * chosen as a learning Neuron. Otherwise, if the Neuron ever stops
     * predicting for any reason, we negatively reinforce the Segments.
     */
    void phaseThree() {
        /// for c, i in cells
        Column[][] columns = this.region.getColumns();
        for (int rowIndex = 0; rowIndex < columns.length; rowIndex++) {
            for (int columnIndex = 0; columnIndex < columns[0].length; columnIndex++) {
                Column column = columns[rowIndex][columnIndex];
                ColumnPosition c = column.getCurrentPosition();
                Neuron[] neurons = column.getNeurons();
                for (int i = 0; i < neurons.length; i++) {
                    /// if learnState(s, i, t) == 1 then
                    if (i == column.getLearningNeuronPosition()) {
                        /// adaptSegments(segmentUpdateList(c, i), true)
                        this.adaptSegments(
                                this.segmentUpdateList.getSegmentUpdate(c, i), true);
                        /// segmentUpdateList(c, i).delete()
                        this.segmentUpdateList.deleteSegmentUpdate(c, i);

                        /// else if predictiveState(c, i, t) == 0 and predictiveState(c, i, t-1)==1 then
                    } else if (neurons[i].getPredictingState() == false
                            && neurons[i].getPreviousPredictingState() == true) {
                        /// adaptSegments(segmentUpdateList(c, i), false)
                        this.adaptSegments(
                                this.segmentUpdateList.getSegmentUpdate(c, i),
                                false);
                        /// segmentUpdateList(c, i).delete()
                        this.segmentUpdateList.deleteSegmentUpdate(c, i);
                    }
                }
            }
        }
    }

    /**
     * Iterates through the Synapses of a SegmentUpdate and reinforces each
     * Synapse. If positiveReinforcement is true then Synapses on the list get
     * their permanenceValues incremented by permanenceIncrease. All other
     * Synapses get their permanenceValue decremented by permanenceDecrease. If
     * positiveReinforcement is false, then Synapses on the list get their
     * permanenceValues decremented by permanenceDecrease. Finally, any Synapses
     * in SegmentUpdate that do not yet exist get added with a permanenceValue
     * of initialPermanence.
     */
    void adaptSegments(SegmentUpdate segmentUpdate,
                       boolean positiveReinforcement) {
        if (segmentUpdate == null) {
            // the neuron being iterated over does not have any segments to
            // update so skip
            return;
        }
        Set> synapsesWithActiveCells = segmentUpdate
                .getSynapsesWithActiveCells();
        Set> synapsesWithDeactiveCells = segmentUpdate
                .getSynpasesWithDeactiveCells();

        if (positiveReinforcement) {
            for (Synapse synapse : synapsesWithActiveCells) {
                synapse.increasePermanence();
            }
            for (Synapse synapse : synapsesWithDeactiveCells) {
                synapse.decreasePermanence();
            }
        } else {
            for (Synapse synapse : synapsesWithActiveCells) {
                synapse.decreasePermanence();
            }
        }
    }

    /**
     * @return The index of the Neuron with the Segment with the greatest number
     * of active Synapses. If no best matching Segment is found, return the
     * Neuron with the least number of active Synapses.
     */
    int getBestMatchingNeuronIndex(Column column) {
        int greatestNumberOfActiveSynapses = 0;
        int bestMatchingNeuronIndex = 0;

        int leastNumberOfSegments = -1;
        int neuronWithLeastSegmentsIndex = -1;
        boolean setNumberOfSegments = false;

        Neuron[] neurons = column.getNeurons();

        for (int i = 0; i < neurons.length; i++) {
            int numberOfSegments = neurons[i].getDistalSegments().size();
            if (!setNumberOfSegments) {
                // following code should be only run the first time
                leastNumberOfSegments = numberOfSegments;
                neuronWithLeastSegmentsIndex = i;
                setNumberOfSegments = true;
            }

            Segment bestSegment = neurons[i].getBestActiveSegment(this.spatialPooler.getAlgorithmStatistics());
            int numberOfActiveSynapses = bestSegment.getNumberOfActiveSynapses();

            if (numberOfActiveSynapses > greatestNumberOfActiveSynapses) {
                greatestNumberOfActiveSynapses = numberOfActiveSynapses;
                bestMatchingNeuronIndex = i;
            }

            // In the case all Neuron's Segments have 0 active Synapses we
            // need to return Neuron with least Segments.
            if (numberOfSegments < leastNumberOfSegments) {
                leastNumberOfSegments = numberOfSegments;
                neuronWithLeastSegmentsIndex = i;
            }
        }

        if (greatestNumberOfActiveSynapses == 0) {
            // All Segments have 0 active Synapses so we nned to return Neuron
            // with least Segments.
            return neuronWithLeastSegmentsIndex;
        }

        return bestMatchingNeuronIndex;
    }

    SegmentUpdateList getSegmentUpdateList() {
        return this.segmentUpdateList;
    }

    int getNewSynapseCount() {
        return this.newSynapseCount;
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n==========================================");
        stringBuilder.append("\n-------TemporalPooler Information---------");
        stringBuilder.append("\n     biological region name: ");
        stringBuilder.append(this.region.getBiologicalName());
        stringBuilder.append("\n     segmentUpdateList size: ");
        stringBuilder.append(this.segmentUpdateList.size());
        stringBuilder.append("\n            newSynapseCount: ");
        stringBuilder.append(this.newSynapseCount);
        stringBuilder.append("\ncurrentLearningNeurons size: ");
        stringBuilder.append(this.currentLearningNeurons.size());
        stringBuilder.append("\n================================");
        String temporalPoolerInformation = stringBuilder.toString();
        return temporalPoolerInformation;
    }

    void computeActiveStateOfAllNeuronsInActiveColumn(Set activeColumns) {
        for (Column column : activeColumns) {

            boolean bottomUpPredicted = false;

            for (Neuron neuron : column.getNeurons()) {

                if (neuron.getPreviousActiveState() == true) {
                    DistalSegment bestSegment = neuron
                            .getBestPreviousActiveSegment(this.spatialPooler.getAlgorithmStatistics());

                    // Question: when is segment ever set to be sequence segment?
                    // Answer:
                    if (bestSegment != null
                            && bestSegment
                            .getSequenceStatePredictsFeedFowardInputOnNextStep()) {
                        bottomUpPredicted = true;
                        neuron.setActiveState(true);
                    }
                }
            }

            if (bottomUpPredicted == false) {
                for (Neuron neuron : column.getNeurons()) {
                    neuron.setActiveState(true);
                }
            }
        }
    }

    void computePredictiveStateOfAllNeurons() {
        Column[][] columns = this.region.getColumns();
        for (int rowIndex = 0; rowIndex < columns.length; rowIndex++) {
            for (int columnIndex = 0; columnIndex < columns[0].length; columnIndex++) {
                Column column = columns[rowIndex][columnIndex];
                for (Neuron neuron : column.getNeurons()) {
                    for (Segment segment : neuron.getDistalSegments()) {
                        if (segment.getActiveState()) {
                            neuron.setPredictingState(true);
                            this.spatialPooler.getAlgorithmStatistics().getTP_activeDistalSegmentsHistoryAndAdd(1);
                            this.predictiveColumnsAtTForTPlus1.add(column
                                    .getCurrentPosition());
                        }
                    }
                }
            }
        }
        this.spatialPooler.getAlgorithmStatistics()
                .getTP_predictionScoreHistoryAndAdd(super.algorithmStatistics
                        .computePredictionScore(this.spatialPooler.getActiveColumnPositions(), this.predictiveColumnsAtTMinus1));
    }

    public int getNumberOfCurrentLearningNeurons() {
        return this.currentLearningNeurons.size();
    }

    /**
     * Save AlgorithmStatistics object into a .JSON file for the current Region.
     */
    public void saveCurrentRegionAlgorithmStatistics(String pathAndFolderNameWithoutEndingBacklash) throws IOException {
        Gson gson = new Gson();
        String algorithmStatisticsInJSON = gson.toJson(this.spatialPooler.getAlgorithmStatistics());
        String finalPathAndFile = pathAndFolderNameWithoutEndingBacklash +
                "/region_" + region.getBiologicalName()
                + "_statistics.json";
        FileInputOutput.saveObjectToTextFile(algorithmStatisticsInJSON,
                finalPathAndFile);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy