/*
* Created on Aug 14, 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.module.procedural.six;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.buffer.IActivationBuffer;
import org.jactr.core.buffer.event.ActivationBufferEvent;
import org.jactr.core.buffer.event.IActivationBufferListener;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunktype.IChunkType;
import org.jactr.core.event.ACTREventDispatcher;
import org.jactr.core.event.IParameterEvent;
import org.jactr.core.logging.Logger;
import org.jactr.core.model.IModel;
import org.jactr.core.model.ModelTerminatedException;
import org.jactr.core.module.AbstractModule;
import org.jactr.core.module.procedural.IProceduralModule;
import org.jactr.core.module.procedural.IProductionInstantiator;
import org.jactr.core.module.procedural.IProductionSelector;
import org.jactr.core.module.procedural.event.IProceduralModuleListener;
import org.jactr.core.module.procedural.event.ProceduralModuleEvent;
import org.jactr.core.module.random.IRandomModule;
import org.jactr.core.module.random.six.DefaultRandomModule;
import org.jactr.core.production.CannotInstantiateException;
import org.jactr.core.production.IInstantiation;
import org.jactr.core.production.IProduction;
import org.jactr.core.production.ISymbolicProduction;
import org.jactr.core.production.action.IAction;
import org.jactr.core.production.action.IBufferAction;
import org.jactr.core.production.action.RemoveAction;
import org.jactr.core.production.condition.AbstractBufferCondition;
import org.jactr.core.production.condition.ChunkCondition;
import org.jactr.core.production.condition.ChunkTypeCondition;
import org.jactr.core.production.condition.IBufferCondition;
import org.jactr.core.production.condition.ICondition;
import org.jactr.core.production.condition.VariableCondition;
import org.jactr.core.production.six.DefaultProduction6;
import org.jactr.core.production.six.ISubsymbolicProduction6;
import org.jactr.core.utils.parameter.IParameterized;
import org.jactr.core.utils.parameter.ParameterHandler;
/**
* <b>strict harvesting</b> is implemented by
* {@link #addProductionInternal(IProduction)}. The {@link IProduction}'s
* {@link ICondition}s are checked. If they are {@link IBufferCondition}, it
* checks to see if {@link IActivationBuffer#isStrictHarvestingEnabled()} and if
* so, ensures that there is an {@link IBufferAction} for that buffer as well,
* if not, an {@link RemoveAction} is added (and therefore the production
* explicitly removes the chunk).
*
* @author harrison
*/
public class DefaultProceduralModule6 extends AbstractModule implements
IProceduralModule6, IParameterized
{
/**
* logger definition
*/
static public final Log LOGGER = LogFactory
.getLog(DefaultProceduralModule6.class);
private ACTREventDispatcher<IProceduralModule, IProceduralModuleListener> _eventDispatcher;
protected Map<String, IProduction> _allProductionsByName;
protected Map<IChunkType, Collection<IProduction>> _allProductionsByChunkType;
protected Map<String, Collection<IProduction>> _ambiguousProductions;
protected ReentrantReadWriteLock _readWriteLock;
protected double _productionFiringTime = 0.05; // 50ms
protected long _productionsFired = 0;
protected boolean _breakExpectedUtilityTiesRandomly = true;
protected double _expectedUtilityNoise;
protected ProductionUtilityComparator _comparator;
protected IRandomModule _randomModule;
private IProductionSelector _selector;
private IProductionInstantiator _instaniator;
private Collection<Map<String, Object>> _provisionalBindings;
public DefaultProceduralModule6()
{
super("procedural");
_allProductionsByName = new TreeMap<String, IProduction>();
_allProductionsByChunkType = new HashMap<IChunkType, Collection<IProduction>>();
_ambiguousProductions = new HashMap<String, Collection<IProduction>>();
_readWriteLock = new ReentrantReadWriteLock();
_eventDispatcher = new ACTREventDispatcher<IProceduralModule, IProceduralModuleListener>();
_selector = new IProductionSelector() {
public IInstantiation select(Collection<IInstantiation> instantiations)
{
if (instantiations.size() > 0) return instantiations.iterator().next();
return null;
}
};
_instaniator = new IProductionInstantiator() {
public Collection<IInstantiation> instantiate(IProduction production,
Collection<Map<String, Object>> provisionalBindings)
throws CannotInstantiateException
{
try
{
return production.instantiateAll(provisionalBindings);
}
catch (Exception e)
{
throw new CannotInstantiateException("Could not instantiate "
+ production + " : " + e.getMessage(), e);
}
}
};
_comparator = new ProductionUtilityComparator() {
@Override
public int compare(IProduction one, IProduction two)
{
int rtn = super.compare(one, two);
if (rtn != 0) return rtn;
if (_breakExpectedUtilityTiesRandomly)
return _randomModule.randomBoolean() ? 1 : -1;
return one.getSymbolicProduction().getName().compareTo(
two.getSymbolicProduction().getName());
}
};
}
public void setProductionSelector(IProductionSelector selector)
{
_selector = selector;
}
public void setProductionInstantiator(IProductionInstantiator instantiator)
{
_instaniator = instantiator;
}
public void addListener(IProceduralModuleListener listener, Executor executor)
{
_eventDispatcher.addListener(listener, executor);
}
public void removeListener(IProceduralModuleListener listener)
{
_eventDispatcher.removeListener(listener);
}
@Override
public void dispose()
{
super.dispose();
try
{
_readWriteLock.writeLock().lock();
// actually dispose of the productions
for (IProduction production : _allProductionsByName.values())
production.dispose();
_ambiguousProductions.clear();
_ambiguousProductions = null;
_allProductionsByChunkType.clear();
_allProductionsByChunkType = null;
_allProductionsByName.clear();
_allProductionsByName = null;
_eventDispatcher.clear();
_eventDispatcher = null;
}
finally
{
_readWriteLock.writeLock().unlock();
}
}
protected void fireProductionCreated(IProduction production)
{
_eventDispatcher.fire(new ProceduralModuleEvent(this,
ProceduralModuleEvent.Type.PRODUCTION_CREATED, production));
}
protected void fireProductionAdded(IProduction production)
{
_eventDispatcher.fire(new ProceduralModuleEvent(this,
ProceduralModuleEvent.Type.PRODUCTION_ADDED, production));
if (Logger.hasLoggers(getModel()))
Logger.log(getModel(), Logger.Stream.PROCEDURAL, "Encoded " + production);
}
protected void fireProductionWillFire(IInstantiation instantiation)
{
_eventDispatcher.fire(new ProceduralModuleEvent(this,
ProceduralModuleEvent.Type.PRODUCTION_WILL_FIRE, instantiation));
if (Logger.hasLoggers(getModel()))
Logger.log(getModel(), Logger.Stream.PROCEDURAL, "Can fire "
+ instantiation);
}
protected void fireProductionFired(IInstantiation instantiation)
{
_eventDispatcher.fire(new ProceduralModuleEvent(this,
ProceduralModuleEvent.Type.PRODUCTION_FIRED, instantiation));
if (Logger.hasLoggers(getModel()))
Logger
.log(getModel(), Logger.Stream.PROCEDURAL, "Fired " + instantiation);
}
protected void fireProductionsMerged(IProduction original,
IProduction duplicate)
{
ArrayList<IProduction> prods = new ArrayList<IProduction>();
prods.add(original);
prods.add(duplicate);
_eventDispatcher.fire(new ProceduralModuleEvent(this,
ProceduralModuleEvent.Type.PRODUCTIONS_MERGED, prods));
}
protected void fireConflictSetAssembled(Collection<IInstantiation> instances)
{
_eventDispatcher.fire(new ProceduralModuleEvent(this,
ProceduralModuleEvent.Type.CONFLICT_SET_ASSEMBLED, instances));
if (Logger.hasLoggers(getModel()))
Logger.log(getModel(), Logger.Stream.PROCEDURAL, "Conflict Set "
+ instances);
}
protected IProduction addProductionInternal(IProduction production)
{
ISymbolicProduction symProd = production.getSymbolicProduction();
/*
* we need all the chunktypes that this production matches against this info
* is used to accelerate conflict set assembly
*/
Set<IChunkType> candidateChunkTypes = new HashSet<IChunkType>();
/*
* in some cases where no chunktype can be infered, we just snag the buffer
* name
*/
Set<String> bufferNames = new HashSet<String>();
Set<String> ambiguousBufferNames = new HashSet<String>();
for (ICondition condition : symProd.getConditions())
if (condition instanceof ChunkTypeCondition)
{
ChunkTypeCondition ctc = (ChunkTypeCondition) condition;
IChunkType chunkType = ctc.getChunkType();
bufferNames.add(ctc.getBufferName());
if (chunkType != null) candidateChunkTypes.add(chunkType);
}
else if (condition instanceof ChunkCondition)
{
ChunkCondition cc = (ChunkCondition) condition;
IChunkType chunkType = cc.getChunk().getSymbolicChunk().getChunkType();
bufferNames.add(cc.getBufferName());
if (chunkType != null) candidateChunkTypes.add(chunkType);
}
else if (condition instanceof AbstractBufferCondition)
{
String bufferName = ((AbstractBufferCondition) condition)
.getBufferName();
if (condition instanceof VariableCondition)
bufferNames.add(bufferName);
/*
* this will catch all queries and variable conditions. These are
* production conditions from which we can't immediately determine the
* chunktype of the buffer contents
*/
ambiguousBufferNames.add(bufferName);
}
/*
* ok, to support strict harvesting, which as far as I can tell is just a
* stylistic implementation. I cannot see an architectural reason for it. It
* just looks like a convenience for modelers so that they don't have to
* manually invoke remove actions.. so, I zip through the buffers, for those
* that have strict harvesting enabled, I check the actions of the
* production. If it contains an action on that buffer, we leave it alone.
* If not, we add a remove, thereby enforcing strict harvesting
*/
Collection<IAction> actions = new ArrayList<IAction>(symProd.getActions());
IModel model = getModel();
for (String bufferName : bufferNames)
{
IActivationBuffer buffer = model.getActivationBuffer(bufferName);
boolean actionFound = false;
if (buffer.isStrictHarvestingEnabled())
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Strict harvesting enabled for " + bufferName
+ ", checking actions of " + symProd.getName());
for (IAction action : actions)
if (action instanceof IBufferAction
&& ((IBufferAction) action).getBufferName().equals(bufferName))
{
actionFound = true;
break;
}
if (!actionFound)
{
if (LOGGER.isDebugEnabled())
LOGGER
.debug(bufferName
+ " requires strict harvest but "
+ symProd.getName()
+ " doesn't operate on the buffer after the match. Adding a remove");
symProd.addAction(new RemoveAction(bufferName));
}
}
}
/*
* figure out all the children of the chunktypes.. since any production that
* could be fired for chunkTypeA could fire for chunkTypeB if chunkTypeB is
* a child (derived from) of chunkTypeA
*/
Set<IChunkType> chunkTypesToProcess = new HashSet<IChunkType>(
candidateChunkTypes);
for (IChunkType chunkType : candidateChunkTypes)
chunkTypesToProcess
.addAll(chunkType.getSymbolicChunkType().getChildren());
_readWriteLock.writeLock().lock();
/*
* make sure the name is unique
*/
String productionName = getSafeName(symProd.getName(),
_allProductionsByName);
symProd.setName(productionName);
production.encode();
/*
* add it to the name map
*/
_allProductionsByName.put(productionName.toLowerCase(), production);
/*
* add it to the chunktype maps
*/
for (IChunkType chunkType : chunkTypesToProcess)
{
Collection<IProduction> productions = _allProductionsByChunkType
.get(chunkType);
if (productions == null)
{
productions = new ArrayList<IProduction>();
_allProductionsByChunkType.put(chunkType, productions);
}
productions.add(production);
}
/*
* now for the ambiguous conditions
*/
for (String bufferName : ambiguousBufferNames)
{
Collection<IProduction> productions = _ambiguousProductions
.get(bufferName);
if (productions == null)
{
productions = new ArrayList<IProduction>();
_ambiguousProductions.put(bufferName, productions);
}
productions.add(production);
}
_readWriteLock.writeLock().unlock();
fireProductionAdded(production);
return production;
}
public Future<IProduction> addProduction(final IProduction production)
{
Callable<IProduction> callable = new Callable<IProduction>() {
public IProduction call() throws Exception
{
return addProductionInternal(production);
}
};
return delayedFuture(callable, getExecutor());
}
protected IProduction removeProductionInternal(IProduction production)
{
ISymbolicProduction symProd = production.getSymbolicProduction();
/*
* we need all the chunktypes that this production matches against this info
* is used to accelerate conflict set assembly
*/
Set<IChunkType> candidateChunkTypes = new HashSet<IChunkType>();
/*
* in some cases where no chunktype can be infered, we just snag the buffer
* name
*/
Set<String> bufferNames = new HashSet<String>();
Set<String> ambiguousBufferNames = new HashSet<String>();
for (ICondition condition : symProd.getConditions())
if (condition instanceof ChunkTypeCondition)
{
ChunkTypeCondition ctc = (ChunkTypeCondition) condition;
IChunkType chunkType = ctc.getChunkType();
bufferNames.add(ctc.getBufferName());
if (chunkType != null) candidateChunkTypes.add(chunkType);
}
else if (condition instanceof ChunkCondition)
{
ChunkCondition cc = (ChunkCondition) condition;
IChunkType chunkType = cc.getChunk().getSymbolicChunk().getChunkType();
bufferNames.add(cc.getBufferName());
if (chunkType != null) candidateChunkTypes.add(chunkType);
}
else if (condition instanceof AbstractBufferCondition)
{
String bufferName = ((AbstractBufferCondition) condition)
.getBufferName();
if (condition instanceof VariableCondition)
bufferNames.add(bufferName);
/*
* this will catch all queries and variable conditions. These are
* production conditions from which we can't immediately determine the
* chunktype of the buffer contents
*/
ambiguousBufferNames.add(bufferName);
}
/*
* figure out all the children of the chunktypes.. since any production that
* could be fired for chunkTypeA could fire for chunkTypeB if chunkTypeB is
* a child (derived from) of chunkTypeA
*/
Set<IChunkType> chunkTypesToProcess = new HashSet<IChunkType>(
candidateChunkTypes);
for (IChunkType chunkType : candidateChunkTypes)
chunkTypesToProcess
.addAll(chunkType.getSymbolicChunkType().getChildren());
_readWriteLock.writeLock().lock();
/*
* make sure the name is unique
*/
String productionName = symProd.getName();
/*
* add it to the name map
*/
_allProductionsByName.remove(productionName.toLowerCase());
/*
* add it to the chunktype maps
*/
for (IChunkType chunkType : chunkTypesToProcess)
{
Collection<IProduction> productions = _allProductionsByChunkType
.get(chunkType);
if (productions != null) productions.remove(production);
}
/*
* now for the ambiguous conditions
*/
for (String bufferName : ambiguousBufferNames)
{
Collection<IProduction> productions = _ambiguousProductions
.get(bufferName);
if (productions != null) productions.remove(production);
}
_readWriteLock.writeLock().unlock();
return production;
}
public Future<IProduction> removeProduction(final IProduction production)
{
Callable<IProduction> callable = new Callable<IProduction>() {
public IProduction call() throws Exception
{
return removeProductionInternal(production);
}
};
return delayedFuture(callable, getExecutor());
}
protected IProduction createProductionInternal(String name)
{
IModel model = getModel();
IProduction production = new DefaultProduction6(model);
production.getSymbolicProduction().setName(
getSafeName(name, _allProductionsByName));
fireProductionCreated(production);
return production;
}
public Future<IProduction> createProduction(final String name)
{
Callable<IProduction> callable = new Callable<IProduction>() {
public IProduction call() throws Exception
{
return createProductionInternal(name);
}
};
return delayedFuture(callable, getExecutor());
}
protected Collection<IInstantiation> getConflictSetInternal(
Collection<IActivationBuffer> buffers)
{
IModel model = getModel();
Set<IProduction> productions = new TreeSet<IProduction>(
new ProductionNameComparator());
/*
* we iterate over the buffers, examining the source chunks and using their
* types to assemble a set of candidate productions..
*/
_readWriteLock.readLock().lock();
for (IActivationBuffer buffer : buffers)
{
for (IChunk chunk : buffer.getSourceChunks())
{
IChunkType chunkType = chunk.getSymbolicChunk().getChunkType();
Collection<IProduction> possible = _allProductionsByChunkType
.get(chunkType);
if (possible != null)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("chunktype " + chunkType + " in buffer "
+ buffer.getName() + " produced " + possible);
productions.addAll(possible);
}
}
// snag all the ambiguous productions
Collection<IProduction> possible = _ambiguousProductions.get(buffer
.getName().toLowerCase());
if (possible != null)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("buffer " + buffer.getName()
+ " produced ambiguous productions " + possible);
productions.addAll(possible);
}
}
_readWriteLock.readLock().unlock();
/*
* we now have every production that could conceivably fire, and some extra
* ones too now we must zip through them trying to instantiate
*/
if (LOGGER.isDebugEnabled() || Logger.hasLoggers(model))
{
StringBuilder sb = new StringBuilder("Considering ");
sb.append(productions.size()).append(" productions for conflict set");
String message = sb.toString();
if (LOGGER.isDebugEnabled()) LOGGER.debug(message);
Logger.log(model, Logger.Stream.PROCEDURAL, message);
}
SortedSet<IInstantiation> keepers = createAndSortInstantiations(productions);
if (LOGGER.isDebugEnabled()) LOGGER.debug("Final conflict set " + keepers);
fireConflictSetAssembled(keepers);
return keepers;
}
/**
* iterates over productions, attempting to instantiate each. Those that can
* be instantiated will be sorted by utility and returned.
*
* @param productions
* @return
*/
protected SortedSet<IInstantiation> createAndSortInstantiations(
Collection<IProduction> productions)
{
IModel model = getModel();
SortedSet<IInstantiation> keepers = new TreeSet<IInstantiation>(_comparator);
/**
* provisional bindings maps the buffer name variables to all the possible
* chunks given the buffer names provided. one problem with using these
* provisional bindings is that they will have references to other buffers
* even if the production doesnt touch them..
*/
Collection<Map<String, Object>> provisionalBindings = computeProvisionalBindings();
StringBuilder message = new StringBuilder();
for (IProduction production : productions)
/*
* only consider those with sufficient utility
*/
// double tmpGain =
// production.getSubsymbolicProduction().getExpectedGain();
// if (tmpGain >= _expectedGainThreshold)
try
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Instantiating " + production);
Collection<IInstantiation> instantiations = _instaniator.instantiate(
production, provisionalBindings);
for (IInstantiation instantiation : instantiations)
{
double noise = _randomModule.logisticNoise(getExpectedUtilityNoise());
ISubsymbolicProduction6 p = (ISubsymbolicProduction6) instantiation
.getSubsymbolicProduction();
double utility = p.getExpectedUtility();
if (Double.isNaN(utility)) utility = p.getUtility();
if (LOGGER.isDebugEnabled())
LOGGER.debug(production + " utility: " + utility + " noise:"
+ noise + " expected utility: " + (utility + noise));
p.setExpectedUtility(utility + noise);
if (LOGGER.isDebugEnabled() || Logger.hasLoggers(model))
{
message.delete(0, message.length());
message.append("Instantiated ").append(production).append(
" expected utility ");
message.append(utility + noise).append(" (").append(noise).append(
" noise)");
String msg = message.toString();
if (LOGGER.isDebugEnabled()) LOGGER.debug(msg);
if (Logger.hasLoggers(model))
Logger.log(model, Logger.Stream.PROCEDURAL, msg);
}
keepers.add(instantiation);
}
}
catch (CannotInstantiateException cie)
{
if (LOGGER.isDebugEnabled() || Logger.hasLoggers(model))
{
StringBuilder sb = new StringBuilder("Could not instantiate ");
sb.append(production).append(" : ").append(cie.getMessage());
String msg = sb.toString();
LOGGER.debug(msg);
Logger.log(model, Logger.Stream.PROCEDURAL, msg);
}
}
// else if (LOGGER.isDebugEnabled() || Logger.hasLoggers(model))
// {
//
// StringBuilder sb = new StringBuilder("Ignoring ");
// sb.append(production).append(" since its expected gain ").append(
// tmpGain);
// sb.append(" is less than threshold ").append(_expectedGainThreshold);
// String message = sb.toString();
// if (LOGGER.isDebugEnabled()) LOGGER.debug(message);
// Logger.log(model, Logger.Stream.PROCEDURAL, message);
// }
return keepers;
}
public Future<Collection<IInstantiation>> getConflictSet(
final Collection<IActivationBuffer> buffers)
{
Callable<Collection<IInstantiation>> callable = new Callable<Collection<IInstantiation>>() {
public Collection<IInstantiation> call() throws Exception
{
return getConflictSetInternal(buffers);
}
};
return delayedFuture(callable, getExecutor());
}
protected IProduction getProductionInternal(String name)
{
_readWriteLock.readLock().lock();
IProduction rtn = _allProductionsByName.get(name.toLowerCase());
_readWriteLock.readLock().unlock();
return rtn;
}
public Future<IProduction> getProduction(final String name)
{
Callable<IProduction> callable = new Callable<IProduction>() {
public IProduction call()
{
return getProductionInternal(name);
}
};
return delayedFuture(callable, getExecutor());
}
protected IInstantiation selectInstantiationInternal(
Collection<IInstantiation> instantiations)
{
return _selector.select(instantiations);
}
public Future<IInstantiation> selectInstantiation(
final Collection<IInstantiation> instantiations)
{
Callable<IInstantiation> callable = new Callable<IInstantiation>() {
public IInstantiation call() throws Exception
{
return selectInstantiationInternal(instantiations);
}
};
return delayedFuture(callable, getExecutor());
}
protected Double fireProductionInternal(IInstantiation instantiation,
double firingTime)
{
try
{
IModel model = getModel();
fireProductionWillFire(instantiation);
if (LOGGER.isDebugEnabled() || Logger.hasLoggers(model))
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("Firing " + instantiation);
Logger.log(model, Logger.Stream.PROCEDURAL, "Firing " + instantiation);
}
instantiation.fire(firingTime);
fireProductionFired(instantiation);
setNumberOfProductionsFired(_productionsFired + 1);
}
catch (ModelTerminatedException mte)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Model has terminated naturally");
return Double.NaN;
}
return instantiation.getActionLatency();
}
public Future<Double> fireProduction(final IInstantiation instantiation,
final double firingTime)
{
Callable<Double> callable = new Callable<Double>() {
public Double call()
{
return fireProductionInternal(instantiation, firingTime);
}
};
return delayedFuture(callable, getExecutor());
}
public double getDefaultProductionFiringTime()
{
return _productionFiringTime;
}
public void setDefaultProductionFiringTime(double firingTime)
{
double old = _productionFiringTime;
_productionFiringTime = firingTime;
if (_eventDispatcher.hasListeners())
_eventDispatcher.fire(new ProceduralModuleEvent(this,
DEFAULT_PRODUCTION_FIRING_TIME, old, firingTime));
}
@Override
public void initialize()
{
// noop
/*
* snag the random module
*/
_randomModule = (IRandomModule) getModel().getModule(IRandomModule.class);
if (_randomModule == null)
_randomModule = DefaultRandomModule.getInstance();
/**
* attach a little something to the buffers so we know when to clear
* provisional bindings
*/
for (IActivationBuffer buffer : getModel().getActivationBuffers())
buffer.addListener(new IActivationBufferListener() {
public void chunkMatched(ActivationBufferEvent abe)
{
// noop
}
public void requestAccepted(ActivationBufferEvent abe)
{
// noop
}
public void sourceChunkAdded(ActivationBufferEvent abe)
{
_provisionalBindings = null;
}
public void sourceChunkRemoved(ActivationBufferEvent abe)
{
_provisionalBindings = null;
}
public void sourceChunksCleared(ActivationBufferEvent abe)
{
_provisionalBindings = null;
}
public void statusSlotChanged(ActivationBufferEvent abe)
{
// TODO Auto-generated method stub
}
public void parameterChanged(IParameterEvent pe)
{
// TODO Auto-generated method stub
}
}, null); // inline
}
public double getExpectedUtilityNoise()
{
return _expectedUtilityNoise;
}
public long getNumberOfProductionsFired()
{
return _productionsFired;
}
public void setExpectedUtilityNoise(double noise)
{
double old = _expectedUtilityNoise;
_expectedUtilityNoise = noise;
if (_eventDispatcher.hasListeners())
_eventDispatcher.fire(new ProceduralModuleEvent(this,
EXPECTED_UTILITY_NOISE, old, noise));
}
public void setNumberOfProductionsFired(long fired)
{
long old = _productionsFired;
_productionsFired = fired;
if (_eventDispatcher.hasListeners())
_eventDispatcher.fire(new ProceduralModuleEvent(this,
NUMBER_OF_PRODUCTIONS_FIRED, old, fired));
}
protected Collection<IProduction> getProductionsInternal()
{
try
{
_readWriteLock.readLock().lock();
return new ArrayList<IProduction>(_allProductionsByName.values());
}
finally
{
_readWriteLock.readLock().unlock();
}
}
public Future<Collection<IProduction>> getProductions()
{
return delayedFuture(new Callable<Collection<IProduction>>() {
public Collection<IProduction> call() throws Exception
{
return getProductionsInternal();
}
}, getExecutor());
}
/**
* @see org.jactr.core.utils.parameter.IParameterized#getParameter(java.lang.String)
*/
public String getParameter(String key)
{
if (NUMBER_OF_PRODUCTIONS_FIRED.equalsIgnoreCase(key))
return "" + getNumberOfProductionsFired();
if (EXPECTED_UTILITY_NOISE.equalsIgnoreCase(key))
return "" + getExpectedUtilityNoise();
if (DEFAULT_PRODUCTION_FIRING_TIME.equalsIgnoreCase(key))
return "" + getDefaultProductionFiringTime();
return null;
}
/**
* @see org.jactr.core.utils.parameter.IParameterized#getPossibleParameters()
*/
public Collection<String> getPossibleParameters()
{
return getSetableParameters();
}
/**
* @see org.jactr.core.utils.parameter.IParameterized#getSetableParameters()
*/
public Collection<String> getSetableParameters()
{
ArrayList<String> rtn = new ArrayList<String>();
rtn.add(EXPECTED_UTILITY_NOISE);
rtn.add(DEFAULT_PRODUCTION_FIRING_TIME);
rtn.add(NUMBER_OF_PRODUCTIONS_FIRED);
return rtn;
}
/**
* @see org.jactr.core.utils.parameter.IParameterized#setParameter(java.lang.String,
* java.lang.String)
*/
public void setParameter(String key, String value)
{
if (NUMBER_OF_PRODUCTIONS_FIRED.equalsIgnoreCase(key))
setNumberOfProductionsFired(ParameterHandler.numberInstance().coerce(
value).longValue());
else if (EXPECTED_UTILITY_NOISE.equalsIgnoreCase(key))
setExpectedUtilityNoise(ParameterHandler.numberInstance().coerce(value)
.doubleValue());
else if (DEFAULT_PRODUCTION_FIRING_TIME.equalsIgnoreCase(key))
setDefaultProductionFiringTime(ParameterHandler.numberInstance().coerce(
value).doubleValue());
else if (LOGGER.isWarnEnabled())
LOGGER.warn("No clue how to set " + key + " to " + value);
}
/**
* in order to handle the iterative nature of the instantiation process in
* addition to the possibility for multiple sources chunks, provisional
* bindings must be created for all the chunk permutations. Ugh.
*/
private Collection<Map<String, Object>> computeProvisionalBindings()
{
// if (_provisionalBindings != null && _provisionalBindings.size() != 0)
// return _provisionalBindings;
//
// if (_provisionalBindings == null)
_provisionalBindings = new ArrayList<Map<String, Object>>();
IModel model = getModel();
Collection<Map<String, Object>> provisionalBindings = new ArrayList<Map<String, Object>>();
Map<String, Object> initialBinding = new TreeMap<String, Object>();
initialBinding.put("=model", model);
provisionalBindings.add(initialBinding);
/*
* with all the buffers this production should match against, we snag their
* sources
*/
for (IActivationBuffer buffer : model.getActivationBuffers())
{
Collection<IChunk> sourceChunks = buffer.getSourceChunks();
if (sourceChunks.size() == 0) continue;
Map<IChunk, Collection<Map<String, Object>>> keyedProvisionalBindings = new HashMap<IChunk, Collection<Map<String, Object>>>();
/*
* if there are more than one source chunk, we need to duplicate all the
* provisional bindings, add the binding for the source chunk and then
* merge the duplicates back into the provisional set
*/
for (IChunk source : sourceChunks)
{
Collection<Map<String, Object>> bindings = provisionalBindings;
// more than one, duplicate.
if (keyedProvisionalBindings.size() != 0)
bindings = copyBindings(provisionalBindings);
// add binding to all bindings
for (Map<String, Object> binding : bindings)
binding.put("=" + buffer.getName(), source);
// store
keyedProvisionalBindings.put(source, bindings);
}
/*
* merge these provisionals back into the full set. if there was only one
* source chunk, it was already added to the provisional binding, so we
* ignore it. If multi, we add all
*/
for (Collection<Map<String, Object>> bindings : keyedProvisionalBindings
.values())
if (bindings != provisionalBindings)
provisionalBindings.addAll(bindings);
}
_provisionalBindings.addAll(provisionalBindings);
return _provisionalBindings;
}
private Collection<Map<String, Object>> copyBindings(
Collection<Map<String, Object>> src)
{
Collection<Map<String, Object>> rtn = new ArrayList<Map<String, Object>>(
src.size());
for (Map<String, Object> map : src)
rtn.add(new TreeMap<String, Object>(map));
return rtn;
}
public void reset()
{
// noop
}
}