/* * 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.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.concurrent.locks.ReentrantReadWriteLock; import javolution.util.FastList; 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.concurrent.ExecutorServices; 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.IConflictSetAssembler; 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.IInstantiation; import org.jactr.core.production.IProduction; import org.jactr.core.production.ISymbolicProduction; import org.jactr.core.production.action.AddAction; import org.jactr.core.production.action.IAction; import org.jactr.core.production.action.IBufferAction; import org.jactr.core.production.action.ModifyAction; 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.utils.parameter.IParameterized; import org.jactr.core.utils.parameter.ParameterHandler; /** * default procedural module. It provides extensibility by using * {@link IProductionInstantiator}, {@link IProductionSelector}, and * {@link IConflictSetAssembler} <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). * * @see http://jactr.org/node/132 * @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 _instantiator; private IConflictSetAssembler _conflictSetAssembler; private boolean _parallelizeInstantiationsEnabled = false; static public final String ENABLE_PARALLEL_INSTANTIATIONS_PARAM = "EnableParallelInstantiations"; // 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>(); setProductionSelector(new DefaultProductionSelector()); setProductionInstantiator(new DefaultProductionInstantiator()); setConflictSetAssembler(new DefaultConflictSetAssembler(false)); _comparator = new ProductionUtilityComparator() { @Override public int compare(IProduction one, IProduction two) { int rtn = super.compare(one, two); if (rtn != 0) return rtn; /* * we only break random ties if the two instantiations (which these * actually are) are not of the same production */ if (_breakExpectedUtilityTiesRandomly && !((IInstantiation) one).getProduction().equals( ((IInstantiation) two).getProduction())) return _randomModule.randomBoolean() ? 1 : -1; // otherwise we maintain insertion order return 0; } }; } public void setProductionSelector(IProductionSelector selector) { if (_selector != null) _selector.setProceduralModule(null); _selector = selector; _selector.setProceduralModule(this); } public IProductionSelector getProductionSelector() { return _selector; } public void setProductionInstantiator(IProductionInstantiator instantiator) { if (_instantiator != null) _instantiator.setProceduralModule(null); _instantiator = instantiator; _instantiator.setProceduralModule(this); } public IProductionInstantiator getProductionInstantiator() { return _instantiator; } public void setConflictSetAssembler(IConflictSetAssembler assembler) { if (_conflictSetAssembler != null) _conflictSetAssembler.setProceduralModule(null); _conflictSetAssembler = assembler; _conflictSetAssembler.setProceduralModule(this); } public IConflictSetAssembler getConflictSetAssembler() { return _conflictSetAssembler; } 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); } public void setParallelInstantiationsEnabled(boolean enable) { _parallelizeInstantiationsEnabled = enable; } public boolean isParallelInstantiationsEnabled() { return _parallelizeInstantiationsEnabled; } 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; ctc.getChunkType(); bufferNames.add(ctc.getBufferName()); // if (chunkType != null) candidateChunkTypes.add(chunkType); } else if (condition instanceof ChunkCondition) { ChunkCondition cc = (ChunkCondition) condition; 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)) { /* * if there is a modify or a remove, we don't have to do anything. */ if (action instanceof RemoveAction || action instanceof ModifyAction || action instanceof AddAction) 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 CompletableFuture<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; ctc.getChunkType(); bufferNames.add(ctc.getBufferName()); // if (chunkType != null) candidateChunkTypes.add(chunkType); } else if (condition instanceof ChunkCondition) { ChunkCondition cc = (ChunkCondition) condition; 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 CompletableFuture<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()); _conflictSetAssembler.getConflictSet(productions); /* * 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); } Collection<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. If * * @param productions * @return */ protected Collection<IInstantiation> createAndSortInstantiations( Collection<IProduction> productions) { // internal for now, should be exposed as a back parameter int minimumBlockSize = 20; List<IInstantiation> keepers = new ArrayList<IInstantiation>(); // some minimum work level, below which we keep it single core if (_parallelizeInstantiationsEnabled && productions.size() > 4) { /* * we split the productions among the threads in the pool and dispatch */ ExecutorCompletionService<Collection<IInstantiation>> ecs = new ExecutorCompletionService<Collection<IInstantiation>>( ExecutorServices.getExecutor(ExecutorServices.POOL)); int size = productions.size(); int tasks = size / minimumBlockSize; tasks += size % minimumBlockSize == 0 ? 0 : 1; FastList<IProduction> working = FastList.newInstance(); working.addAll(productions); for (int i = 0; i < tasks; i++) { int start = i * minimumBlockSize; int end = Math.min(i * minimumBlockSize + minimumBlockSize, size); InstantiationTask it = new InstantiationTask( working.subList(start, end), getProductionInstantiator(), getModel(), _randomModule, getExpectedUtilityNoise()); ecs.submit(it); } /* * everyone is churning away.. let's harvest and sort as we go. */ for (int i = 0; i < tasks; i++) try { Collection<IInstantiation> result = ecs.take().get(); keepers.addAll(result); } catch (Exception e) { LOGGER.error("Failed to instantiatie productions in parallel: ", e); } FastList.recycle(working); } else try { InstantiationTask it = new InstantiationTask(productions, getProductionInstantiator(), getModel(), _randomModule, getExpectedUtilityNoise()); keepers.addAll(it.call()); } catch (Exception e) { LOGGER.error("Failed to instantiatie productions in serial: ", e); } Collections.sort(keepers, _comparator); return keepers; } public CompletableFuture<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 CompletableFuture<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 CompletableFuture<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); } setNumberOfProductionsFired(_productionsFired + 1); instantiation.fire(firingTime); fireProductionFired(instantiation); } catch (ModelTerminatedException mte) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Model has terminated naturally"); return Double.NaN; } return instantiation.getActionLatency(); } public CompletableFuture<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 CompletableFuture<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(); if (ENABLE_PARALLEL_INSTANTIATIONS_PARAM.equalsIgnoreCase(key)) return "" + isParallelInstantiationsEnabled(); 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); rtn.add(ENABLE_PARALLEL_INSTANTIATIONS_PARAM); 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 (ENABLE_PARALLEL_INSTANTIATIONS_PARAM.equalsIgnoreCase(key)) setParallelInstantiationsEnabled(ParameterHandler.booleanInstance() .coerce(value).booleanValue()); else if (LOGGER.isWarnEnabled()) LOGGER.warn(String.format( "%s doesn't recognize %s. Available parameters : %s", getClass() .getSimpleName(), key, getSetableParameters())); } // /** // * 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<VariableBindings> computeProvisionalBindings( // IProduction production) // { // IModel model = getModel(); // Collection<VariableBindings> returnedProvisionalBindings = new // ArrayList<VariableBindings>(); // Collection<VariableBindings> provisionalBindings = new // ArrayList<VariableBindings>(); // // VariableBindings initialBinding = new VariableBindings(); // initialBinding.bind("=model", model); // provisionalBindings.add(initialBinding); // // /* // * with all the buffers this production should match against, we snag their // * sources // */ // for (ICondition condition : production.getSymbolicProduction() // .getConditions()) // if (condition instanceof IBufferCondition // && !(condition instanceof QueryCondition)) // { // IActivationBuffer buffer = model // .getActivationBuffer(((IBufferCondition) condition).getBufferName()); // // Collection<IChunk> sourceChunks = buffer.getSourceChunks(); // // if (sourceChunks.size() == 0) continue; // // Map<IChunk, Collection<VariableBindings>> keyedProvisionalBindings = new // HashMap<IChunk, Collection<VariableBindings>>(); // // /* // * 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<VariableBindings> bindings = provisionalBindings; // // more than one, duplicate. // if (keyedProvisionalBindings.size() != 0) // bindings = copyBindings(provisionalBindings); // // // add binding to all bindings // for (VariableBindings binding : bindings) // binding.bind("=" + buffer.getName(), source, buffer); // // // 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<VariableBindings> bindings : keyedProvisionalBindings // .values()) // if (bindings != provisionalBindings) // provisionalBindings.addAll(bindings); // } // // returnedProvisionalBindings.addAll(provisionalBindings); // // return returnedProvisionalBindings; // } // // private Collection<VariableBindings> copyBindings( // Collection<VariableBindings> src) // { // Collection<VariableBindings> rtn = new ArrayList<VariableBindings>( // src.size()); // for (VariableBindings map : src) // rtn.add(map.clone()); // return rtn; // } public void reset() { // noop } }