org.jactr.core.buffer.AbstractActivationBuffer.java Source code

Java tutorial

Introduction

Here is the source code for org.jactr.core.buffer.AbstractActivationBuffer.java

Source

/*
 * Created on Aug 13, 2005 Copyright (C) 2001-5, Anthony Harrison anh23@pitt.edu
 * (jactr.org) This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the License,
 * or (at your option) any later version. This library is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU Lesser General Public License for more details. You should have
 * received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
package org.jactr.core.buffer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.buffer.event.ActivationBufferEvent;
import org.jactr.core.buffer.event.IActivationBufferListener;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunk.ISubsymbolicChunk;
import org.jactr.core.chunk.four.ISubsymbolicChunk4;
import org.jactr.core.chunk.four.Link;
import org.jactr.core.event.ACTREventDispatcher;
import org.jactr.core.logging.Logger;
import org.jactr.core.model.IModel;
import org.jactr.core.module.IModule;
import org.jactr.core.utils.parameter.IParameterized;
import org.jactr.core.utils.parameter.ParameterHandler;

/**
 * basic implementation of an activation buffer that handles the spreading of
 * activation to chunks. this is thread safe.
 * 
 * @author developer
 */
public abstract class AbstractActivationBuffer implements IActivationBuffer, IParameterized {

    /**
     * Logger definition
     */

    static private final transient Log LOGGER = LogFactory.getLog(AbstractActivationBuffer.class);

    final private IModel _model;

    final private IModule _module;

    final private Collection<IChunk> _activatedChunks;

    private double _activation = 0;

    private double _goalValue = 0;

    private boolean _strictHarvestingEnabled = true;

    final private String _name;

    final private ACTREventDispatcher<IActivationBuffer, IActivationBufferListener> _eventDispatcher;

    final private ReentrantReadWriteLock _lock = new ReentrantReadWriteLock();

    /**
     * Comment for <code>ACTIVATION</code>
     */
    static public final String ACTIVATION_PARAM = "Activation";

    /**
     * Comment for <code>GOAL_VALUE</code>
     */
    static public final String GOAL_VALUE_PARAM = "G";

    static public final String STRICT_HARVESTING_PARAM = "StrictHarvestingEnabled";

    static private final String[] SETTABLE = { ACTIVATION_PARAM, GOAL_VALUE_PARAM, STRICT_HARVESTING_PARAM };

    static private final String[] GETTABLE = { ACTIVATION_PARAM, GOAL_VALUE_PARAM, STRICT_HARVESTING_PARAM };

    public AbstractActivationBuffer(String name, IModel model, IModule module) {
        _name = name;
        _model = model;
        _module = module;
        _activatedChunks = new ArrayList<IChunk>();
        _eventDispatcher = new ACTREventDispatcher<IActivationBuffer, IActivationBufferListener>();
    }

    public void dispose() {
        try {
            getLock().writeLock().lock();
            _activatedChunks.clear();
            _eventDispatcher.clear();
        } finally {
            getLock().writeLock().unlock();
        }
    }

    protected ReentrantReadWriteLock getLock() {
        return _lock;
    }

    public void matched(IChunk chunk) {
        if (chunk == null)
            return;
        boolean signal = false;
        try {
            _lock.writeLock().lock();
            signal = matchedInternal(chunk);
        } finally {
            _lock.writeLock().unlock();
        }

        if (signal)
            if (_eventDispatcher.hasListeners())
                _eventDispatcher
                        .fire(new ActivationBufferEvent(this, ActivationBufferEvent.Type.CHUNK_MATCHED, chunk));
    }

    /**
     * return true if the matched event should be fired
     * 
     * @param chunk
     * @return
     */
    protected boolean matchedInternal(IChunk chunk) {
        return true;
    }

    final public void setActivation(double activation) {
        double oldAct = _activation;
        try {
            _lock.writeLock().lock();
            _activation = activation;
        } finally {
            _lock.writeLock().unlock();
        }

        if (_eventDispatcher.hasListeners())
            _eventDispatcher.fire(new ActivationBufferEvent(this, ActivationBufferEvent.Type.PARAMETER_CHANGED,
                    ACTIVATION_PARAM, oldAct, activation));
    }

    final public double getActivation() {
        try {
            _lock.readLock().lock();
            return _activation;
        } finally {
            _lock.readLock().unlock();
        }
    }

    final public void setG(double g) {
        double oldValue = _goalValue;
        try {
            _lock.writeLock().lock();
            _goalValue = g;
        } finally {
            _lock.writeLock().unlock();
        }
        if (_eventDispatcher.hasListeners())
            _eventDispatcher.fire(new ActivationBufferEvent(this, ActivationBufferEvent.Type.PARAMETER_CHANGED,
                    GOAL_VALUE_PARAM, oldValue, g));
    }

    final public double getG() {
        try {
            _lock.readLock().lock();
            return _goalValue;
        } finally {
            _lock.readLock().unlock();
        }
    }

    final public void setStrictHarvestingEnabled(boolean enabled) {
        try {
            getLock().writeLock().lock();
            _strictHarvestingEnabled = enabled;
        } finally {
            getLock().writeLock().unlock();
        }
    }

    final public boolean isStrictHarvestingEnabled() {
        try {
            getLock().readLock().lock();
            return _strictHarvestingEnabled;
        } finally {
            getLock().readLock().unlock();
        }
    }

    final protected ACTREventDispatcher<IActivationBuffer, IActivationBufferListener> getEventDispatcher() {
        return _eventDispatcher;
    }

    /**
     * will call removeSourceChunkInternal for all the chunks and then recalculate
     * activation
     * 
     * @see org.jactr.core.buffer.IActivationBuffer#clear()
     */
    public void clear() {
        Collection<IChunk> old = null;
        try {
            _lock.writeLock().lock();
            old = clearInternal();
        } catch (RuntimeException re) {
            LOGGER.error("exception while clearing buffer " + getName(), re);
            throw re;
        } finally {
            _lock.writeLock().unlock();
        }

        // notify
        if (old.size() != 0) {
            if (_eventDispatcher.hasListeners())
                _eventDispatcher.fire(new ActivationBufferEvent(this, old));

            if (Logger.hasLoggers(getModel()))
                Logger.log(getModel(), Logger.Stream.BUFFER, "cleared " + getName());
        }
    }

    /**
     * called within the lock
     * 
     * @return the chunks that were removed
     */
    protected Collection<IChunk> clearInternal() {
        Collection<IChunk> cleared = getSourceChunks();

        for (IChunk chunk : cleared)
            if (removeSourceChunkInternal(chunk))
                BufferUtilities.unmarkContained(chunk, this);

        calculateAndSpreadSourceActivation();
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("cleared " + cleared);
        return cleared;
    }

    /**
     * spread activation to the contents, if any..
     */
    final protected void calculateAndSpreadSourceActivation() {
        try {
            _lock.writeLock().lock();
            clearActivatedChunks();
            _activatedChunks.addAll(spreadActivation());
        } finally {
            _lock.writeLock().unlock();
        }
    }

    /**
     * spread activation to all the chunks that are connected to the source chunk
     * 
     * @return a collection of all the chunks that have had their source
     *         activation incremented
     */
    final protected Collection<IChunk> spreadActivation() {
        /*
         * we use an array list which WILL permit duplicates. Why? shouldn't
         * something appearing N times in a source chunk get N times the source
         * activation?
         */
        Collection<IChunk> rtn = new ArrayList<IChunk>();
        Collection<Link> iLinks = new ArrayList<Link>();
        /*
         * we need to get each chunk that the source contains
         */
        for (IChunk sourceChunk : getSourceChunks()) {
            ISubsymbolicChunk sourceChunkSub = sourceChunk.getSubsymbolicChunk();
            if (sourceChunkSub instanceof ISubsymbolicChunk4) {
                iLinks.clear();
                ((ISubsymbolicChunk4) sourceChunkSub).getIAssociations(iLinks);

                /*
                 * this chunk is J, so we need I
                 */
                for (Link iLink : iLinks)
                    rtn.add(iLink.getIChunk());
            }

            if (rtn.size() != 0) {
                // now we have them all, let's apply the activation
                double sourceActivation = getActivation() / rtn.size();
                for (IChunk chunk : rtn) {
                    // unlikely, but possible..
                    if (chunk.hasBeenDisposed())
                        continue;
                    ISubsymbolicChunk ssc = chunk.getSubsymbolicChunk();
                    double activation = ssc.getSourceActivation() + sourceActivation;
                    ssc.setSourceActivation(activation);
                }
            }
        }

        return rtn;
    }

    /**
     * 
     *
     */
    final private void clearActivatedChunks() {
        if (_activatedChunks.size() == 0)
            return;

        double sourceActivation = getActivation() / _activatedChunks.size();
        for (IChunk chunk : _activatedChunks) {
            // a chunk may have been disposed of by now...
            if (chunk.hasBeenDisposed())
                continue;
            ISubsymbolicChunk ssc = chunk.getSubsymbolicChunk();
            double activation = ssc.getSourceActivation() - sourceActivation;
            ssc.setSourceActivation(activation);
        }
        _activatedChunks.clear();
    }

    /**
     * return the source chunk that was actually inserted into the buffer (i.e. a
     * copy of chunkToInsert if chunkToInsert has already been encoded).
     * 
     * @param chunkToInsert
     *          the chunk to be inserted, will never be null nor already in the
     *          buffer
     * @return actual chunk inserted. if null, no events are fired or activation
     *         spread
     */
    abstract protected IChunk addSourceChunkInternal(IChunk chunkToInsert);

    /**
     * add chunk to the buffer. if addSourceChunkInternal(IChunk) returns not
     * null, activation is spread and an event is fired
     * 
     * @see org.jactr.core.buffer.IActivationBuffer#addSourceChunk(org.jactr.core.chunk.IChunk)
     */
    public IChunk addSourceChunk(IChunk c) {
        if (c == null)
            return null;

        IChunk added = contains(c);
        if (added != null)
            return added;

        /*
         * moved event firing out of synchronization
         */
        try {
            _lock.writeLock().lock();
            added = addSourceChunkInternal(c);
            if (added != null)
                calculateAndSpreadSourceActivation();
        } finally {
            _lock.writeLock().unlock();
        }

        if (added != null) {
            BufferUtilities.markContained(added, this, getActivation());

            if (LOGGER.isDebugEnabled())
                LOGGER.debug("addSourceChunkInternal returned true, dispatching and calculating activation");
            if (_eventDispatcher.hasListeners())
                _eventDispatcher
                        .fire(new ActivationBufferEvent(this, ActivationBufferEvent.Type.SOURCE_ADDED, added));

            if (Logger.hasLoggers(getModel()))
                Logger.log(getModel(), Logger.Stream.BUFFER, "Added " + added + " to " + getName());
        }

        return added;
    }

    /**
     * do the actual work of removing chunkToRemove from the buffer.
     * 
     * @param chunkToRemove
     *          chunk that is in the buffer, never null
     * @return true if activation is to be reset and an event fired
     */
    abstract protected boolean removeSourceChunkInternal(IChunk chunkToRemove);

    /**
     * remove the chunk from the buffer. if removeSourceChunkInternal(IChunk)
     * returns true activation is reset and event fired
     * 
     * @see org.jactr.core.buffer.IActivationBuffer#removeSourceChunk(org.jactr.core.chunk.IChunk)
     */
    public void removeSourceChunk(IChunk c) {
        if (c == null)
            return;

        boolean shouldRemove = false;

        /*
         * is the chunk actually in the buffer?
         */
        for (IChunk chunk : getSourceChunks())
            if (chunk.equals(c)) {
                shouldRemove = true;
                break;
            }

        if (!shouldRemove)
            return;

        /*
         * remove firing from synch
         */
        try {
            _lock.writeLock().lock();
            shouldRemove = removeSourceChunkInternal(c);
            if (shouldRemove)
                calculateAndSpreadSourceActivation();
        } finally {
            _lock.writeLock().unlock();
        }

        if (shouldRemove) {
            BufferUtilities.unmarkContained(c, this);

            if (LOGGER.isDebugEnabled())
                LOGGER.debug("removeSourceChunkInternal returned true, dispatching and calculating activation");
            if (_eventDispatcher.hasListeners())
                _eventDispatcher
                        .fire(new ActivationBufferEvent(this, ActivationBufferEvent.Type.SOURCE_REMOVED, c));

            if (Logger.hasLoggers(getModel()))
                Logger.log(getModel(), Logger.Stream.BUFFER, "Removed " + c + " from " + getName());
        }
    }

    final public IChunk getSourceChunk() {
        try {
            _lock.readLock().lock();
            return getSourceChunkInternal();
        } finally {
            _lock.readLock().unlock();
        }
    }

    /**
     * return the source chunk from the internal backing store
     * 
     * @return
     */
    abstract protected IChunk getSourceChunkInternal();

    final public Collection<IChunk> getSourceChunks() {
        try {
            _lock.readLock().lock();
            return getSourceChunksInternal();
        } finally {
            _lock.readLock().unlock();
        }
    }

    abstract protected Collection<IChunk> getSourceChunksInternal();

    public IChunk contains(IChunk c) {
        for (IChunk chunk : getSourceChunks())
            if (chunk.equalsSymbolic(c))
                return chunk;
        return null;
    }

    public void addListener(IActivationBufferListener abl, Executor executor) {
        _eventDispatcher.addListener(abl, executor);
    }

    public void removeListener(IActivationBufferListener abl) {
        _eventDispatcher.removeListener(abl);
    }

    public String getName() {
        return _name;
    }

    public IModel getModel() {
        return _model;
    }

    public boolean handlesEncoding() {
        return false;
    }

    public IModule getModule() {
        return _module;
    }

    /**
     * @see org.jactr.core.utils.parameter.IParameterized#getSetableParameters()
     * @return
     */
    public Collection<String> getSetableParameters() {
        return Arrays.asList(SETTABLE);
    }

    /**
     * @see org.jactr.core.utils.parameter.IParameterized#getPossibleParameters()
     * @return
     */
    public Collection<String> getPossibleParameters() {
        return Arrays.asList(GETTABLE);
    }

    /**
     * @param parameter
     * @see org.jactr.core.utils.parameter.IParameterized#getParameter(java.lang.String)
     * @return
     */
    public String getParameter(String parameter) {
        String rtn = null;

        if (parameter.equalsIgnoreCase(ACTIVATION_PARAM))
            rtn = ParameterHandler.numberInstance().toString(getActivation());
        else if (parameter.equalsIgnoreCase(GOAL_VALUE_PARAM))
            rtn = ParameterHandler.numberInstance().toString(getG());
        else if (parameter.equalsIgnoreCase(STRICT_HARVESTING_PARAM))
            rtn = "" + isStrictHarvestingEnabled();

        return rtn;
    }

    /**
     * @param parameter
     * @param value
     * @see org.jactr.core.utils.parameter.IParameterized#setParameter(java.lang.String,
     *      String)
     */
    public void setParameter(String parameter, String value) {
        if (parameter.equalsIgnoreCase(ACTIVATION_PARAM))
            setActivation(ParameterHandler.numberInstance().coerce(value).doubleValue());
        else if (parameter.equalsIgnoreCase(GOAL_VALUE_PARAM))
            setG(ParameterHandler.numberInstance().coerce(value).doubleValue());
        else if (parameter.equalsIgnoreCase(STRICT_HARVESTING_PARAM))
            setStrictHarvestingEnabled(ParameterHandler.booleanInstance().coerce(value));
    }

    public void initialize() {
        // noop
    }

    public String toString() {
        return getName();
    }
}