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

jasima.core.experiment.Experiment Maven / Gradle / Ivy

Go to download

JAva SImulatior for MAnufacturing and logistics - A framework for discrete event simulation and computer experiments with a main focus on modelling and analyzing logistic/manufacturing systems.

There is a newer version: 1.3.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2010-2013 Torsten Hildebrandt and jasima contributors
 *
 * This file is part of jasima, v1.0.
 *
 * jasima 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.
 *
 * jasima 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with jasima.  If not, see .
 *
 * $Id: Experiment.java 123 2013-07-08 09:27:56Z [email protected] $
 *******************************************************************************/
package jasima.core.experiment;

import jasima.core.experiment.Experiment.ExperimentEvent;
import jasima.core.util.ConsolePrinter;
import jasima.core.util.Util;
import jasima.core.util.observer.Notifier;
import jasima.core.util.observer.NotifierAdapter;
import jasima.core.util.observer.NotifierListener;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * An Experiment is something that produces results depending on various
 * parameters. The usual lifecycle is to create an experiment, set parameters to
 * their proper values, execute the experiment by calling it's runExperiment()
 * method. After execution a set of results are available using the getResults()
 * methods.
 * 

* Experiments are not supposed to be run more than once, e.g., to run multiple * replications of an experiment (see {@link MultipleReplicationExperiment}) you * have to create multiple instances. Therefore experiments should be cloneable. *

* This class is intended as the base class for Experiments doing something * useful. This class only has a single parameter "initialSeed" (see * getInitialSeed()/setInitialSeed()). This parameter is supposed to be used as * the starting value for all (pseudo) random number generation activities at * experiment runtime. This means two experiments having the same initialSeed * and all other experiment parameters being the same should behave * deterministically and produce exactly the same results. *

* The only result produced by this class is "runTime" (type Double), which * measures the real time required to execute an experiment. *

* Experiments can have listeners registered, which are informed of an * experiment's start and completion and can be used by subclasses to provide * additional events. * * @author Torsten Hildebrandt * @version "$Id: Experiment.java 73 2013-01-08 17:16:19Z [email protected]$" */ public abstract class Experiment implements Cloneable, Serializable, Notifier { private static final long serialVersionUID = -5981694222402234985L; public static final String RUNTIME = "runTime"; public static final String EXP_ABORTED = "expAborted"; public static final String EXCEPTION = "exception"; public static final String EXCEPTION_MESSAGE = "exceptionMessage"; /** * Simple base class for events used by the notification mechanism. */ public static class ExperimentEvent { } public static final ExperimentEvent EXPERIMENT_STARTING = new ExperimentEvent(); public static final ExperimentEvent EXPERIMENT_INITIALIZED = new ExperimentEvent(); public static final ExperimentEvent EXPERIMENT_BEFORE_RUN = new ExperimentEvent(); public static final ExperimentEvent EXPERIMENT_AFTER_RUN = new ExperimentEvent(); public static final ExperimentEvent EXPERIMENT_DONE = new ExperimentEvent(); public static final ExperimentEvent EXPERIMENT_COLLECT_RESULTS = new ExperimentEvent(); public static final ExperimentEvent EXPERIMENT_FINISHING = new ExperimentEvent(); public static final ExperimentEvent EXPERIMENT_FINISHED = new ExperimentEvent(); public enum ExpMsgCategory { OFF, ERROR, WARN, INFO, DEBUG, TRACE, ALL } public static class ExpPrintEvent extends ExperimentEvent { public ExpPrintEvent(Experiment exp, ExpMsgCategory category, String message) { super(); this.exp = exp; this.category = category; this.message = message; } public final Experiment exp; public final ExpMsgCategory category; public final String message; } // parameters private int nestingLevel = 0; private String name = null; private long initialSeed = 0xd23284FEA3L; // just an arbitrary default seed // used during run private long runTimeReal; protected int aborted; protected Map resultMap; // used during event notification public Map results; public static class UniqueNamesCheckingHashMap extends LinkedHashMap { private static final long serialVersionUID = -6783419937586790463L; @Override public Object put(String key, Object value) { if (containsKey(key)) throw new RuntimeException("Map already contains value '" + key + "'."); return super.put(key.intern(), value); } } public Experiment() { super(); } protected void init() { aborted = 0; } protected void beforeRun() { } protected abstract void performRun(); protected void done() { } protected void finish() { } public void runExperiment() { try { runTimeReal = System.currentTimeMillis(); if (numListener() > 0) fire(EXPERIMENT_STARTING); init(); if (numListener() > 0) fire(EXPERIMENT_INITIALIZED); beforeRun(); if (numListener() > 0) fire(EXPERIMENT_BEFORE_RUN); performRun(); if (numListener() > 0) fire(EXPERIMENT_AFTER_RUN); done(); if (numListener() > 0) fire(EXPERIMENT_DONE); } finally { runTimeReal = System.currentTimeMillis() - runTimeReal; } // build result map resultMap = new UniqueNamesCheckingHashMap(); produceResults(); if (numListener() > 0) { results = resultMap; fire(EXPERIMENT_COLLECT_RESULTS); results = null; } // give experiments and listener a chance to view/modify results finish(); if (numListener() > 0) { results = resultMap; fire(EXPERIMENT_FINISHING); results = null; } // we are done, don't change results any more resultMap = Collections.unmodifiableMap(resultMap); if (numListener() > 0) fire(EXPERIMENT_FINISHED); } public final Map getResults() { return resultMap; } protected void produceResults() { resultMap.put(RUNTIME, (runTimeReal / 1000.0d)); resultMap.put(EXP_ABORTED, aborted); } /** * Retrieves a list containing the name and current value of this class's * properties. */ public Map getPropsWithValues() { Map props = new LinkedHashMap(); PropertyDescriptor[] pds = Util.findWritableProperties(this); for (PropertyDescriptor pd : pds) { try { props.put(pd.getName(), pd.getReadMethod().invoke(this)); } catch (Exception e) { throw new RuntimeException(pd.getName(), e); } } return props; } /** * Triggers a print event of category "info". * * @param message * The message to print. * @see #print(ExpMsgCategory, String) */ public void print(String message) { print(ExpMsgCategory.INFO, message); } /** * Triggers a print event of the given category. If an appropriate listener * is installed, this should produce an output of {@code message}. * * @param message * The message to print. * @see ConsolePrinter */ public void print(ExpMsgCategory category, String message) { if (numListener() > 0) { fire(new ExpPrintEvent(this, category, message)); } } /** * Prints the results of this experiments to {@link System#out}. * * @param res */ public final void printResults(Map res) { ConsolePrinter.printResults(this, res); } /** * Prints the results of this experiments to {@link System#out}. * * @param res */ public final void printResults() { ConsolePrinter.printResults(this, getResults()); } public String toString() { return getName() == null ? super.toString() : getName(); } @Override public Experiment clone() throws CloneNotSupportedException { Experiment c = (Experiment) super.clone(); c.adapter = null; for (int i = 0; i < numListener(); i++) { NotifierListener clone = Util .cloneIfPossible(getNotifierListener(i)); c.addNotifierListener(clone); } return c; } /** * This is the same as clone(), just without throwing the checked exception * CloneNotSupportedException. If such an exception occurs, it is wrapped in * a RuntimeException. */ public Experiment silentClone() { try { return clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } // /** // * Runs the experiment and returns results. This method implements the // * {@link Callable} interface. // */ // @Override // public Map call() throws Exception { // runExperiment(); // return getResults(); // } /** * Sets the nesting level. This method is only for internal purposes. */ public void nestingLevel(int nestingLevel) { this.nestingLevel = nestingLevel; } /** * The level in the call hierarchy this experiment is executed in. * Experiments that spawn new sub-experiments (like * {@link MultipleReplicationExperiment}) are required to increase their * children's nestingLevel by 1. */ public int nestingLevel() { return nestingLevel; } /** * This method is used internally to decide how to execute an experiment. If * this method returns true (default), it does not spawn child experiments * to produce it's results. */ public boolean isLeafExperiment() { return true; } public void setName(String name) { this.name = name; } public String getName() { return name; } public long getInitialSeed() { return initialSeed; } public void setInitialSeed(long s) { initialSeed = s; } // // // event notification // // private NotifierAdapter adapter = null; @Override public void addNotifierListener( NotifierListener listener) { if (adapter == null) adapter = new NotifierAdapter(this); adapter.addNotifierListener(listener); } @Override public NotifierListener getNotifierListener( int index) { return adapter.getNotifierListener(index); } @Override public void removeNotifierListener( NotifierListener listener) { adapter.removeNotifierListener(listener); } protected void fire(ExperimentEvent event) { if (adapter != null) adapter.fire(event); } @Override public int numListener() { return adapter == null ? 0 : adapter.numListener(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy