All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.sandia.cognition.framework.concurrent.MultithreadedCognitiveModel Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * 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 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 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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy