gov.sandia.cognition.framework.concurrent.MultithreadedCognitiveModel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cognitive-foundry Show documentation
Show all versions of cognitive-foundry Show documentation
A single jar with all the Cognitive Foundry components.
/*
* File: MultithreadedCognitiveModel.java
* Authors: Zachary Benz
* Company: Sandia National Laboratories
* Project: Cognitive Framework Lite
*
* Copyright Jan 9, 2008, Sandia Corporation. Under the terms of Contract
* DE-AC04-94AL85000, there is a non-exclusive license for use of this work by
* or on behalf of the U.S. Government. Export of this program may require a
* license from the United States Government. See CopyrightHistory.txt for
* complete details.
*
*
*/
package gov.sandia.cognition.framework.concurrent;
import gov.sandia.cognition.framework.CognitiveModelInput;
import gov.sandia.cognition.framework.CognitiveModelState;
import gov.sandia.cognition.framework.CognitiveModule;
import gov.sandia.cognition.framework.CognitiveModuleFactory;
import gov.sandia.cognition.framework.CognitiveModuleState;
import gov.sandia.cognition.framework.DefaultSemanticIdentifierMap;
import gov.sandia.cognition.framework.lite.AbstractCognitiveModelLite;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* This class provides a multithreaded implementation of the CognitiveModel
* interface. The implementation is lite, in that modules cannot be dynamically
* added or removed from the model. Multihtreading is employed at the level
* of the evaluation of modules during an update call. During update, each
* module sequentially performs a readState in preparation for evaluation, and
* then evaluation of the modules is performed concurrently using a threadpool.
* The change in state of each module is written out sequentially following
* the completion of all module evaluations.
*
* @author Zachary Benz
* @since 2.0
*/
public class MultithreadedCognitiveModel
extends AbstractCognitiveModelLite
{
/**
* Implements a Callable class for executing the evaluation of a
* CognitiveModule on a thread
*
* @author Zachary Benz
* @since 2.0
*/
protected static class ModuleEvaluator
implements Callable
{
/** The module to run the evaluation on */
private ConcurrentCognitiveModule module;
/** CognitiveModelState to use when evaluating module */
private CognitiveModelState modelState;
/** CognitiveModuleState to use when evaluating module */
private CognitiveModuleState previousModuleState;
/**
* Creates a new instance of ModuleEvaluator that can be supplied to a
* thread for executing a module evaluation
*
* @param module The module to the run the evaluation on
* @param modelState CognitiveModelState to use when evaluating module
* @param previousModuleState CognitiveModuleState to use when
* evaluating the module
*/
ModuleEvaluator(
ConcurrentCognitiveModule module,
CognitiveModelState modelState,
CognitiveModuleState previousModuleState)
{
this.module = module;
this.modelState = modelState;
this.previousModuleState = previousModuleState;
}
@Override
public Exception call()
{
try
{
// Evaluate the module
module.evaluate();
}
catch (Exception e)
{
// Caught an exception
return e;
}
// No exception caught, so return null
return null;
}
}
/** The CognitiveModules that are part of this model. */
private ConcurrentCognitiveModule[] modules = null;
/** A thread pool for managing concurrent evaluation of modules */
private ExecutorService fixedThreadPool;
/**
* Creates a new instance of MultithreadedCognitiveModel. It instantiates
* new CognitiveModules from all of the given CognitiveModuleFactories
*
* @param numThreadsInPool Number of threads to use in thread pool
* @param moduleFactories The CognitiveModuleFactories used to create the
* CognitiveModules for this model
*/
public MultithreadedCognitiveModel(
final int numThreadsInPool,
CognitiveModuleFactory... moduleFactories)
{
this(numThreadsInPool, Arrays.asList(moduleFactories));
}
/**
* Creates a new instance of MultithreadedCognitiveModel. It instantiates
* new CognitiveModules from all of the given CognitiveModuleFactories
*
* @param numThreadsInPool Number of threads to use in thread pool
* @param moduleFactories The CognitiveModuleFactories used to create the
* CognitiveModules for this model
*/
public MultithreadedCognitiveModel(
final int numThreadsInPool,
Iterable extends CognitiveModuleFactory> moduleFactories)
{
super();
this.setSemanticIdentifierMap(
new DefaultSemanticIdentifierMap());
// Instantiate the CognitiveModules from the given factories.
LinkedList moduleList =
new LinkedList();
for ( CognitiveModuleFactory factory : moduleFactories )
{
if( factory != null )
{
CognitiveModule module = factory.createModule(this);
if (module instanceof ConcurrentCognitiveModule)
{
moduleList.add((ConcurrentCognitiveModule)module);
}
else
{
throw new IllegalArgumentException(
"MultithreadedCognitiveModel: All modules must " +
"conform to ConcurrentCognitiveModule interface, " +
"but " + factory.getClass().toString() + " created an" +
" object of type " + module.getClass().toString() +
", which doesn't conform.");
}
}
}
// Set the modules.
this.setModules(moduleList);
this.resetCognitiveState();
// Configure thread pool for execution
this.setFixedThreadPool(Executors.newFixedThreadPool(numThreadsInPool));
}
/**
* Updates the state of the model from the new input.
*
* @param input The input to the model.
*/
public void update(CognitiveModelInput input)
{
// Set the input on the state.
this.state.setInput(input);
CognitiveModuleState[] moduleStates = this.state.getModuleStatesArray();
// ReadState doesn't support concurrency, as state can be
// dynamically filled in on read due to sparse nature of state
// (i.e. attempting to read something that doesn't exist yet will
// cause it to become initialized, thus changing the state)
for (int i = 0; i < this.numModules; i++)
{
this.modules[i].readState(this.state, moduleStates[i]);
}
// Evaluate can occur concurrently across modules
Collection> moduleEvaluators =
new ArrayList>(this.numModules);
for (int i = 0; i < this.numModules; i++)
{
moduleEvaluators.add(new ModuleEvaluator(this.modules[i],
this.state, moduleStates[i]));
}
// Invoke the evaluators and block until all are completed
List> results;
try
{
results = this.getFixedThreadPool().invokeAll(moduleEvaluators);
}
catch (InterruptedException e)
{
throw new IllegalThreadStateException(e.toString());
}
// Check for exceptions in the results
Exception firstException = null;
int exceptionCounter = 0;
for (Future result : results)
{
Exception exception;
try
{
// Attempting to obtain the result can fail
exception = result.get();
}
catch (Exception e)
{
// Set the exception to the exception generated when attempting
// to get the result exception
exception = e;
}
if (exception != null)
{
exceptionCounter++;
if (firstException == null)
{
firstException = exception;
}
}
}
if (exceptionCounter > 0)
{
String errorMessage = "MultithreadedCognitiveModel.update: " +
exceptionCounter + " exceptions encountered during module " +
"evaluation";
throw new IllegalArgumentException(errorMessage + "; throwing " +
"first exception encountered", firstException);
}
// WriteState happens sequentially at end after concurrent evaluation
// of modules has occurred.
for (int i = 0; i < this.numModules; i++)
{
// We are operating by side effect with this line of code... it's
// changing the this.state.getModuleStatesArray return members.
moduleStates[i] =
this.modules[i].writeState(this.state);
}
this.fireModelStateChangedEvent();
}
/**
* Gets the modules in the model.
*
* @return The modulese that are part of the model.
*/
public List getModules()
{
return Arrays.asList(this.modules);
}
/**
* Sets the modules to use in the model.
*
* Note: This is declared private because it cannot be changed from its
* initial value without breaking the model.
*
* This function should be called exactly once.
*
* @param moduleCollection The modules to use in the model
*/
private void setModules(
Collection extends ConcurrentCognitiveModule> moduleCollection)
{
this.numModules = moduleCollection.size();
this.modules = moduleCollection.toArray(
new ConcurrentCognitiveModule[this.numModules]);
}
/**
* Returns the thread pool for executing module evaluations
*
* @return The thread pool for executing module evaluations
*/
private ExecutorService getFixedThreadPool()
{
return fixedThreadPool;
}
/**
* Sets the thread pool for executing module evaluations
*
* @param fixedThreadPool The thread pool to use for executing module
* evaluations
*/
private void setFixedThreadPool(
final ExecutorService fixedThreadPool)
{
this.fixedThreadPool = fixedThreadPool;
}
}