/* * JABM - Java Agent-Based Modeling Toolkit * Copyright (C) 2013 Steve Phelps * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program 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 General Public License for more details. */ package net.sourceforge.jabm.init; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashMap; import java.util.LinkedHashMap; import net.sourceforge.jabm.EventScheduler; import net.sourceforge.jabm.Simulation; import net.sourceforge.jabm.SimulationController; import net.sourceforge.jabm.SpringSimulationController; import net.sourceforge.jabm.event.RandomVariateInitialisedEvent; import net.sourceforge.jabm.util.Properties; import org.apache.log4j.Logger; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PropertyOverrideConfigurer; import bsh.EvalError; import bsh.Interpreter; import cern.jet.random.engine.RandomEngine; /** * <p> * A simulation factory which initialises the values of pre-specified * Spring bean properties by drawing randomly from the specified probability * distribution. The bean properties are designated as random variates * by creating a configuration file which specifies the mapping between * each variate and its corresponding distribution. * </p> * * <p> * Currently the following distributions are supported. * </p> * * <table border="1"> * <tr> * <td><code>N</code></td> <td>Normal distribution</td> * </tr> * <tr> * <td><code>U</code></td> <td>Uniform distribution</td> * </tr> * </table> * * <p> * The parameters of the distribution are specified in brackets after * the abbreviation. For example the following configuration: * </p> * * <code> * agent.epsilon = U(0.2, 0.5)<br> * agent.wealth = N(200.0, 10.0)<br> * </code> * * <p> * specifies that the agent's epsilon parameter is uniformly distributed * between 0.2 and 0.5 and the agent's wealth is normally distributed with * mean 200 and standard deviation 10. * </p> * * @author Steve Phelps * @see java.util.Properties * @see cern.jet.random.AbstractDistribution * */ public class RandomVariateInitialiser extends SpringSimulationFactory implements InitializingBean { protected Object configFileNamePrefix = ""; protected String configFileName = "config/random-variate.properties"; protected RandomEngine prng; protected HashMap<String,String> expressionBindings; protected EventScheduler eventScheduler; static Logger logger = Logger.getLogger(RandomVariateInitialiser.class); public RandomVariateInitialiser() { super(); } @Override public Simulation initialise(SimulationController simulationController) { this.eventScheduler = simulationController; java.util.Properties variateBindings = new java.util.Properties(); for(String variable : expressionBindings.keySet()) { Number value = evaluate(expressionBindings.get(variable), variateBindings); variateBindings.put(variable, value + ""); logger.info(variable + " = " + value); fireEvent(variable, value); } PropertyOverrideConfigurer configurer = new PropertyOverrideConfigurer(); configurer.setProperties(variateBindings); configurer.setLocalOverride(true); configurer .postProcessBeanFactory((ConfigurableListableBeanFactory) ((SpringSimulationController) simulationController) .getBeanFactory()); return super.initialise(simulationController); } public void parseConfigFile() throws FileNotFoundException, IOException { expressionBindings = new LinkedHashMap<String, String>(); File f = new File(configFileNamePrefix + configFileName); Properties properties = new Properties(); properties.load(new FileInputStream(f)); for(Object property : properties.keySet()) { String expression = (String) properties.get(property); expressionBindings.put(property.toString(), expression); } } public Number evaluate(String expression, java.util.Properties variateBindings) { try { Interpreter bsh = initialiseBeanShell(variateBindings); logger.debug("Evalating " + expression); return (Number) bsh.eval(expression); } catch (EvalError e) { throw new RuntimeException(e); } } public Interpreter initialiseBeanShell(java.util.Properties variateBindings) throws EvalError { Interpreter bsh = new Interpreter(); bsh.set("prng", this.prng); bsh.eval("double N(double mean, double std) " + "{ return new cern.jet.random.Normal(mean,std,prng).nextDouble(); }"); bsh.eval("double U(double min, double max) " + "{ return new cern.jet.random.Uniform(min,max,prng).nextDouble(); }"); for (Object variate : variateBindings.keySet()) { String var = variate.toString(); bsh.set(var, Double.parseDouble(variateBindings.get(var).toString())); } return bsh; } protected void fireEvent(String variableName, Number value) { if (eventScheduler != null) { eventScheduler.fireEvent(new RandomVariateInitialisedEvent( variableName, value.doubleValue())); } } public String getConfigFileName() { return configFileName; } /** * The name of the configuration file which specifies which * bean properties are to be treated as random variates and their * corresponding distribution. This file is in the standard * format for a Properties file. * * @param configFileName * @see java.util.Properties */ @Required public void setConfigFileName(String configFileName) { this.configFileName = configFileName; } public RandomEngine getPrng() { return prng; } /** * The pseudo-random number generator to be used for drawing all * variates drawn by this initialiser. * * @param prng */ @Required public void setPrng(RandomEngine prng) { this.prng = prng; } @Override public void afterPropertiesSet() throws Exception { parseConfigFile(); } public EventScheduler getEventScheduler() { return eventScheduler; } /** * If this property is set then the initialiser will fire * a RandomVariateInitialisedEvent each time a * random variate is initialised. * * @param eventScheduler The scheduler used to manage the event queue. * * @see net.sourceforge.jabm.event.RandomVariateInitialisedEvent * @see net.sourceforge.jabm.EventScheduler */ public void setEventScheduler(EventScheduler eventScheduler) { this.eventScheduler = eventScheduler; } public Object getConfigFileNamePrefix() { return configFileNamePrefix; } public void setConfigFileNamePrefix(Object configFileNamePrefix) { this.configFileNamePrefix = configFileNamePrefix; } }