/* * Created on Oct 25, 2006 Copyright (C) 2001-6, 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.chunk.basic; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.TreeSet; import java.util.concurrent.locks.Lock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunk.ISubsymbolicChunk; import org.jactr.core.chunk.IllegalChunkStateException; import org.jactr.core.chunk.event.ChunkEvent; import org.jactr.core.event.ParameterEvent; import org.jactr.core.module.declarative.four.learning.IBaseLevelActivationEquation; import org.jactr.core.module.declarative.four.learning.IDeclarativeLearningModule4; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.core.utils.parameter.CollectionParameterHandler; import org.jactr.core.utils.parameter.ParameterHandler; import org.jactr.core.utils.references.IOptimizedReferences; import org.jactr.core.utils.references.IReferences; public abstract class AbstractSubsymbolicChunk implements ISubsymbolicChunk { /** * logger definition */ static private final Log LOGGER = LogFactory .getLog(AbstractSubsymbolicChunk.class); final protected IChunk _parentChunk; protected IReferences _referenceList; protected double _creationTime; // encoding // time protected double _baseLevelActivation; protected double _spreadingActivation; protected double _sourceActivation; protected double _totalActivation; protected int _timesInContext; protected int _timesNeeded; protected double _lastActivationComputationTime = -1; protected IBaseLevelActivationEquation _baseLevelActiationEquation; public AbstractSubsymbolicChunk(IChunk parent) { _parentChunk = parent; try { _referenceList = IReferences.Factory.get().newInstance(); /* * can we set the optimization? */ IDeclarativeLearningModule4 dlm = (IDeclarativeLearningModule4) parent .getModel().getModule(IDeclarativeLearningModule4.class); if (dlm != null) _baseLevelActiationEquation = dlm.getBaseLevelActivationEquation(); if (dlm != null && _referenceList instanceof IOptimizedReferences) ((IOptimizedReferences) _referenceList).setOptimizationLevel(dlm .getOptimizationLevel()); } catch (Exception e) { throw new IllegalChunkStateException("Could not create references ", e); } } protected Lock readLock() { return _parentChunk.getReadLock(); } protected Lock writeLock() { return _parentChunk.getWriteLock(); } public void accessed(double time) { try { writeLock().lock(); _referenceList.addReferenceTime(ACTRRuntime.getRuntime().getClock( _parentChunk.getModel()).getTime()); } finally { writeLock().unlock(); } if (_parentChunk.hasListeners()) _parentChunk.dispatch(new ChunkEvent(_parentChunk, ChunkEvent.Type.ACCESSED)); } public void dispose() { try { writeLock().lock(); _referenceList.clear(); } finally { writeLock().unlock(); } } /** * non-locking since this will set creation time * * @see org.jactr.core.chunk.ISubsymbolicChunk#encode(double) */ public void encode(double when) { if (_parentChunk.isEncoded()) throw new IllegalChunkStateException("Chunk has already been encoded"); setCreationTime(ACTRRuntime.getRuntime().getClock(_parentChunk.getModel()) .getTime()); } public double getActivation() { refreshActivationValues(); try { readLock().lock(); return _totalActivation; } finally { readLock().unlock(); } } public double getBaseLevelActivation() { refreshActivationValues(); try { readLock().lock(); return _baseLevelActivation; } finally { readLock().unlock(); } } public double getCreationTime() { try { readLock().lock(); return _creationTime; } finally { readLock().unlock(); } } public IReferences getReferences() { return _referenceList; } public double getSourceActivation() { try { readLock().lock(); return _sourceActivation; } finally { readLock().unlock(); } } public double getSpreadingActivation() { refreshActivationValues(); try { readLock().lock(); return _spreadingActivation; } finally { readLock().unlock(); } } public int getTimesInContext() { try { readLock().lock(); return _timesInContext; } finally { readLock().unlock(); } } public int getTimesNeeded() { try { readLock().lock(); return _timesNeeded; } finally { readLock().unlock(); } } public void incrementTimesInContext() { setTimesInContext(getTimesInContext() + 1); } public void incrementTimesNeeded() { setTimesNeeded(getTimesNeeded() + 1); } public void setActivation(double act) { double old = 0; try { writeLock().lock(); old = _totalActivation; _totalActivation = act; } finally { writeLock().unlock(); } if (_parentChunk.hasParameterListeners()) _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), ACTIVATION, old, act)); } public void setBaseLevelActivation(double base) { double old = 0; try { writeLock().lock(); old = _baseLevelActivation; _baseLevelActivation = base; } finally { writeLock().unlock(); } if (_parentChunk.hasParameterListeners()) _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), BASE_LEVEL_ACTIVATION, old, base)); } public void setCreationTime(double time) { if (_parentChunk.isEncoded()) throw new IllegalChunkStateException( "Creation time cannot be changed after encoding"); double old = 0; try { writeLock().lock(); old = _creationTime; _creationTime = time; } finally { writeLock().unlock(); } if (_parentChunk.hasParameterListeners()) _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), CREATION_TIME, old, time)); } public void setSourceActivation(double source) { double old = 0; try { writeLock().lock(); old = _sourceActivation; _sourceActivation = source; } finally { writeLock().unlock(); } if (_parentChunk.hasParameterListeners()) _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), SOURCE_ACTIVATION, old, source)); } public void setSpreadingActivation(double spread) { double old = 0; try { writeLock().lock(); old = _spreadingActivation; _spreadingActivation = spread; } finally { writeLock().unlock(); } if (_parentChunk.hasParameterListeners()) _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), SPREADING_ACTIVATION, old, spread)); } public void setTimesInContext(int context) { int old = 0; try { writeLock().lock(); old = _timesInContext; _timesInContext = context; } finally { writeLock().unlock(); } if (_parentChunk.hasParameterListeners()) _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), TIMES_IN_CONTEXT, old, context)); } public void setTimesNeeded(int needed) { int old = 0; try { writeLock().lock(); old = _timesNeeded; _timesNeeded = needed; } finally { writeLock().unlock(); } if (_parentChunk.hasParameterListeners()) _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), TIMES_NEEDED, old, needed)); } public String getParameter(String key) { String rtn = null; if (CREATION_TIME.equalsIgnoreCase(key)) rtn = ParameterHandler.numberInstance().toString(getCreationTime()); else if (TIMES_NEEDED.equalsIgnoreCase(key)) rtn = ParameterHandler.numberInstance().toString(getTimesNeeded()); else if (TIMES_IN_CONTEXT.equalsIgnoreCase(key)) rtn = ParameterHandler.numberInstance().toString(getTimesInContext()); else if (REFERENCE_COUNT.equalsIgnoreCase(key)) rtn = ParameterHandler.numberInstance().toString( getReferences().getNumberOfReferences()); else if (REFERENCE_TIMES.equalsIgnoreCase(key)) { double[] times = getReferences().getTimes(); // make sure they are sorted Arrays.sort(times); Collection<Number> nTimes = new ArrayList<Number>(times.length); for (double time : times) nTimes.add(time); // make sure they are sorted CollectionParameterHandler<Number> aph = new CollectionParameterHandler<Number>( ParameterHandler.numberInstance()); return aph.toString(nTimes); } else if (BASE_LEVEL_ACTIVATION.equalsIgnoreCase(key)) rtn = ParameterHandler.numberInstance() .toString(getBaseLevelActivation()); else if (SPREADING_ACTIVATION.equalsIgnoreCase(key)) rtn = ParameterHandler.numberInstance() .toString(getSpreadingActivation()); else if (SOURCE_ACTIVATION.equalsIgnoreCase(key)) rtn = ParameterHandler.numberInstance().toString(getSourceActivation()); else if (ACTIVATION.equalsIgnoreCase(key)) rtn = ParameterHandler.numberInstance().toString(getActivation()); return rtn; } public Collection<String> getPossibleParameters() { ArrayList<String> params = new ArrayList<String>(); params.add(CREATION_TIME); params.add(TIMES_NEEDED); params.add(TIMES_IN_CONTEXT); params.add(REFERENCE_COUNT); params.add(REFERENCE_TIMES); params.add(BASE_LEVEL_ACTIVATION); params.add(SPREADING_ACTIVATION); params.add(SOURCE_ACTIVATION); params.add(ACTIVATION); return params; } public Collection<String> getSetableParameters() { return getPossibleParameters(); } public void setParameter(String key, String value) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Attempting to set " + key + " to " + value); if (CREATION_TIME.equalsIgnoreCase(key)) setCreationTime(ParameterHandler.numberInstance().coerce(value) .doubleValue()); else if (TIMES_NEEDED.equalsIgnoreCase(key)) setTimesNeeded(ParameterHandler.numberInstance().coerce(value).intValue()); else if (TIMES_IN_CONTEXT.equalsIgnoreCase(key)) setTimesInContext(ParameterHandler.numberInstance().coerce(value) .intValue()); else if (REFERENCE_COUNT.equalsIgnoreCase(key)) { long referenceCount = ParameterHandler.numberInstance().coerce(value) .longValue(); IReferences references = getReferences(); double[] oldTimes = references.getTimes(); long oldCount = references.getNumberOfReferences(); references.clear(); /* * create referenceCount references from creation time to now */ double min = getCreationTime(); double step = (ACTRRuntime.getRuntime().getClock( getParentChunk().getModel()).getTime() - min) / referenceCount; for (int i = 0; i < referenceCount; i++) references.addReferenceTime(getCreationTime() + (i * step)); _lastActivationComputationTime = -1; if (_parentChunk.hasParameterListeners()) { _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), REFERENCE_COUNT, oldCount, referenceCount)); _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), REFERENCE_TIMES, oldTimes, references.getTimes())); } } else if (REFERENCE_TIMES.equalsIgnoreCase(key)) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Attempting to set reference times with " + value); CollectionParameterHandler<Number> cph = new CollectionParameterHandler<Number>( ParameterHandler.numberInstance()); Collection<Number> times = cph.coerce(value); // let's make sure they are sorted.. TreeSet<Double> refTimes = new TreeSet<Double>(); for (Number time : times) refTimes.add(time.doubleValue()); IReferences references = getReferences(); double[] oldTimes = references.getTimes(); /* * if count was previously set, we need to maintain it.. */ references.setNumberOfReferences(Math.max(0, references .getNumberOfReferences() - refTimes.size())); /* * now we'll add these times */ for (Double time : refTimes) references.addReferenceTime(time); _lastActivationComputationTime = -1; if (_parentChunk.hasParameterListeners()) _parentChunk.dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime() .getClock(_parentChunk.getModel()).getTime(), REFERENCE_TIMES, oldTimes, references.getTimes())); } else if (BASE_LEVEL_ACTIVATION.equalsIgnoreCase(key)) setBaseLevelActivation(ParameterHandler.numberInstance().coerce(value) .doubleValue()); else if (SPREADING_ACTIVATION.equalsIgnoreCase(key)) setSpreadingActivation(ParameterHandler.numberInstance().coerce(value) .doubleValue()); else if (SOURCE_ACTIVATION.equalsIgnoreCase(key)) setSourceActivation(ParameterHandler.numberInstance().coerce(value) .doubleValue()); else if (ACTIVATION.equalsIgnoreCase(key)) setActivation(ParameterHandler.numberInstance().coerce(value) .doubleValue()); } protected void refreshActivationValues() { double now = ACTRRuntime.getRuntime().getClock(_parentChunk.getModel()) .getTime(); if (now > _lastActivationComputationTime && _baseLevelActiationEquation != null) { _lastActivationComputationTime = now; calculateValues(); } } private void calculateValues() { setBaseLevelActivation(computeBaseLevelActivation()); setSpreadingActivation(computeSpreadingActivation()); setActivation(getBaseLevelActivation() + getSpreadingActivation()); } private double computeBaseLevelActivation() { if (_baseLevelActiationEquation == null) return _baseLevelActivation; return _baseLevelActiationEquation.computeBaseLevelActivation(_parentChunk .getModel(), _parentChunk); } abstract protected double computeSpreadingActivation(); protected IChunk getParentChunk() { return _parentChunk; } }