Java tutorial
/* * This file is part of JGAP. * * JGAP offers a dual license model containing the LGPL as well as the MPL. * * For licensing information please see the file license.txt included with JGAP * or have a look at the top of class org.jgap.Chromosome which representatively * includes the JGAP license policy applicable for any file delivered with JGAP. */ package org.jgap; import java.io.*; import java.util.*; import org.apache.commons.lang.builder.*; import org.jgap.audit.*; import org.jgap.data.config.*; import org.jgap.event.*; import org.jgap.impl.*; import org.jgap.util.*; /** * The Configuration class represents the current configuration of * plugins and parameters necessary to execute the genetic algorithm (such * as fitness function, natural selector, genetic operators, and so on). * <p> * Note that, while during setup, the settings, flags, and other * values may be set multiple times. But once the lockSettings() method * is invoked, they cannot be changed. The default behavior of the * Genotype constructor is to invoke this method, meaning that * once a Configuration object is passed to a Genotype, it cannot * be subsequently modified. There is no mechanism for unlocking * the settings once they are locked. * <p> * Not all configuration options are required. See the documentation * for each of the respective mutator methods to determine whether * it is required to provide a value for that setting, and what the * setting will default to if not. * * @author Neil Rotstan * @author Klaus Meffert * @since 1.0 */ public class Configuration implements Configurable, Serializable, ICloneable, Comparable { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.102 $"; /** * Constant for class name of JGAP Factory to use. Use as: * System.setProperty(PROPERTY_JGAPFACTORY_CLASS, "myJGAPFactory"); * If none such property set, class JGAPFactory will be used. */ public static final String PROPERTY_JGAPFACTORY_CLASS = "JGAPFACTORYCLASS"; public static final String PROPERTY_FITFUNC_INST = "JGAPFITFUNCINST"; public static final String PROPERTY_BFITFNC_INST = "JGAPBFITFNCINST"; public static final String PROPERTY_FITEVAL_INST = "JGAPFITEVALINST"; public static final String PROPERTY_SAMPLE_CHROM_INST = "JGAPSAMPLECHRMINST"; public static final String PROPERTY_EVENT_MGR_INST = "JGAPEVNTMGRINST"; /** * Constants for toString() */ public static final String S_CONFIGURATION = "Configuration"; public static final String S_CONFIGURATION_NAME = "Configuration name"; public static final String S_POPULATION_SIZE = "Population size"; public static final String S_MINPOPSIZE = "Minimum pop. size [%]"; public static final String S_CHROMOSOME_SIZE = "Chromosome size"; public static final String S_SAMPLE_CHROM = "Sample Chromosome"; public static final String S_SIZE = "Size"; public static final String S_TOSTRING = "toString"; public static final String S_RANDOM_GENERATOR = "Random generator"; public static final String S_EVENT_MANAGER = "Event manager"; public static final String S_NONE = "none"; public static final String S_CONFIGURATION_HANDLER = "Configuration handler"; public static final String S_FITNESS_FUNCTION = "Fitness function"; public static final String S_FITNESS_EVALUATOR = "Fitness evaluator"; // public static final String S_POPCONSTANT_SELECTOR = "Constant Population Selector"; public static final String S_GENETIC_OPERATORS = "Genetic operators"; public static final String S_NATURAL_SELECTORS = "Natural Selectors"; public static final String S_PRE = "pre"; public static final String S_POST = "post"; /** * Contains a bag of custom properties. Can be empty. * A custom property is such that is not included in the standard JGAP * configuration. * * @since 3.3.1 */ // private Map m_propertyBag; private ConfigurationConfigurable m_config = new ConfigurationConfigurable(); /** * References the current fitness function that will be used to evaluate * chromosomes during the natural selection process. Note that only this * or the bulk fitness function may be set - the two are mutually exclusive. * * @author Neil Rotstan * @since 1.0 */ private FitnessFunction m_objectiveFunction; /** * The fitness evaluator. See interface class FitnessEvaluator for details. * * @since 2.0 (since 1.1 in class Genotype) */ private FitnessEvaluator m_fitnessEvaluator; /** * Performs the evolution. */ private IBreeder m_breeder; /** * Minimum size guaranteed for population. If zero or below then no ensurance. * * @author Klaus Meffert */ private int m_minPercentageSizePopulation; /** * References the current bulk fitness function that will be used to * evaluate chromosomes (in bulk) during the natural selection * process. Note that only this or the normal fitness function * may be set - the two are mutually exclusive. * * @author Neil Rotstan * @since 1.0 */ private BulkFitnessFunction m_bulkObjectiveFunction; // /** // * If population size should be kept constant then this selector determines // * which of the chromosomes to select into the next generation. // * // * @author Klaus Meffert // * @since 3.2.2 // */ // private INaturalSelector m_popConstantSelector; /** * References a Chromosome that serves as a sample of the Gene setup * that is to be used. Each gene in the Chromosome should be represented * with the desired Gene type. * * @author Neil Rotstan * @since 1.0 */ private IChromosome m_sampleChromosome; /** * References the random number generator implementation that is to be * used for the generation of any random numbers during the various * genetic operations and processes. * * @author Neil Rotstan * @since 1.0 */ private RandomGenerator m_randomGenerator; /** * References the event manager that is to be used for the notification * of genetic events and the management of event subscribers. * * @author Neil Rotstan * @since 1.0 */ private IEventManager m_eventManager; /** * References the chromosome pool, if any, that is to be used to pool * discarded Chromosome instances so that they may be recycled later, * thereby saving memory and the time to construct them from scratch. * * @author Neil Rotstan * @since 1.0 */ private transient IChromosomePool m_chromosomePool; /** * Stores all of the GeneticOperator implementations that are to be used * to operate upon the chromosomes of a population prior to natural * selection. Operators will be executed in the order that they are * added to this list. * * @author Klaus Meffert * @since 1.1 */ private List m_geneticOperators; /** * The number of genes that will be stored in each chromosome in the * population. */ private int m_chromosomeSize; /** * Indicates whether the settings of this Configuration instance have * been locked. Prior to locking, the settings may be set and reset * as desired. Once this flag is set to true, no settings may be * altered. */ private boolean m_settingsLocked; /** * Ordered chain of NaturalSelector's which will be executed before applying * Genetic Operators. * * @author Klaus Meffert * @since 1.1 */ private ChainOfSelectors m_preSelectors; /** * Ordered chain of NaturalSelector's which will be executed after applying * Genetic Operators. * * @author Klaus Meffert * @since 1.1 */ private ChainOfSelectors m_postSelectors; /** * Should the fittest chromosome in the population be preserved to the next * generation when evolving (in Genotype.evolve()) ? */ private boolean m_preserveFittestIndividual; /** * How many chromosomes should be selected from previous generation? * The missing chromosomes will be filled up with randomly created new * ones. * 1 = all */ private double m_selectFromPrevGen; /** * Indicates how many times the evolve()-method in class Genotype has been * called. Represents the number of the current population. * * @author Klaus Meffert * @since 2.2 */ private int m_generationNr; /** * The Configuration handler for this Configurable. * * @author Siddhartha Azad * @since 2.3 */ private transient RootConfigurationHandler m_conHandler; /** * Informative name for output. * * @author Klaus Meffert * @since 2.3 */ private String m_name; /** * True: population size will be kept constant at specified size in * configuration. False: population size will grow dependently on used * NaturalSelector's and GeneticOperator's. * Default is TRUE * * @author Klaus Meffert * @since 2.4 */ private boolean m_keepPopulationSizeConstant; /** * Holds the central configurable factory for creating default objects. * * @author Klaus Meffert * @since 2.6 */ private IJGAPFactory m_factory; /** * See Chromosome class, field m_alwaysCalculate, for description * * @author Klaus Meffert * @since 3.2.2 */ private boolean m_alwaysCalculateFitness; private transient String threadKey; /** * True: Use unique keys, especially in Chromosomes, to allow tracking and * monitoring of evolution progress. * * @author Klaus Meffert * @since 3.5 */ private boolean m_uniqueKeysActive; /** * Unique ID for a configuration to distinguish it from other configurations * instantiated within the same thread. * * @author Klaus Meffert * @since 3.01 */ private String m_id; /** * * @author Klaus Meffert * @since 3.5 */ private IEvolutionMonitor m_monitor; public Configuration() { this("", null); } /** * Initialize with default values. * * @param a_id unique id for the configuration within the current thread * @param a_name informative name of the configuration, may be null * * @author Neil Rotstan * @author Klaus Meffert * @since 1.0 */ public Configuration(String a_id, String a_name) { // m_propertyBag = new Hashtable(); m_id = a_id; setName(a_name); makeThreadKey(); m_preSelectors = new ChainOfSelectors(this); m_postSelectors = new ChainOfSelectors(this); m_selectFromPrevGen = 1.0d; // Use synchronized list for distributed computing. // ------------------------------------------------ m_geneticOperators = new Vector(); m_conHandler = new RootConfigurationHandler(); m_conHandler.setConfigurable(this); m_keepPopulationSizeConstant = true; m_alwaysCalculateFitness = false; // Create factory for being able to configure the used default objects, // like random generators or fitness evaluators. // -------------------------------------------------------------------- String clazz = System.getProperty(PROPERTY_JGAPFACTORY_CLASS); if (clazz != null && clazz.length() > 0) { try { m_factory = (IJGAPFactory) Class.forName(clazz).newInstance(); } catch (Throwable ex) { throw new RuntimeException( "Class " + clazz + " could not be instantiated" + " as type IJGAPFactory", ex); } } else { m_factory = new JGAPFactory(false); } } /** * Constructs a configuration with an informative name but without a unique * ID. This practically prevents more than one configurations to be * instantiated within the same thread. * * @param a_name informative name of the configuration, may be null * * @author Klaus Meffert */ public Configuration(final String a_name) { this(); setName(a_name); } /** * Reads in the configuration from the given file. * * @param a_configFileName the config file from which to load the * configuration * @param a_ignore just there to create distinct signatures :-( * * @throws ConfigException * @throws InvalidConfigurationException * * @author Siddhartha Azad * @since 2.3 */ public Configuration(final String a_configFileName, boolean a_ignore) throws ConfigException, InvalidConfigurationException { this(); ConfigFileReader.instance().setFileName(a_configFileName); // Set the configuration statically for constructing classes by the // default constructor. // ---------------------------------------------------------------- Genotype.setStaticConfiguration(this); // Read in the config, thus creating instances of configurable classes // by invoking their default constructor. // ------------------------------------------------------------------- getConfigurationHandler().readConfig(); } /** * SHOULD NOT BE NECESSARY TO CALL UNDER NORMAL CIRCUMSTANCES. May be useful * for unit tests.<p> * Reset the configuration so that re-setting parameters such as fitness * function is possible (without calling this method, an overwriting of a * previously set fitness function results in a RuntimeException). * * @author Klaus Meffert * @since 3.0 */ public static void reset() { reset(""); } /** * Reset the configuration so that re-setting parameters such as fitness * function is possible (without calling this method, an overwriting of a * previously set fitness function results in a RuntimeException). * * @param a_id a hopefully unique id of the configuration * * @author Klaus Meffert * @since 3.0 */ public static void reset(final String a_id) { String threadKey = getThreadKey(Thread.currentThread(), a_id); System.setProperty(threadKey + Configuration.PROPERTY_FITFUNC_INST, ""); System.setProperty(threadKey + Configuration.PROPERTY_BFITFNC_INST, ""); System.setProperty(threadKey + Configuration.PROPERTY_FITEVAL_INST, ""); System.setProperty(threadKey + Configuration.PROPERTY_SAMPLE_CHROM_INST, ""); System.setProperty(threadKey + Configuration.PROPERTY_EVENT_MGR_INST, ""); } /** * See Configuration.reset(). * @param a_propName the property to reset * * @author Klaus Meffert * @since 3.0 */ public static void resetProperty(final String a_propName) { resetProperty(a_propName, ""); } public static void resetProperty(final String a_propName, final String a_id) { String threadKey = getThreadKey(Thread.currentThread(), a_id); System.setProperty(threadKey + a_propName, ""); } /** * @param a_name informative name of the configuration * * @author Klaus Meffert * @since 2.3 */ public void setName(final String a_name) { m_name = a_name; } /** * @return informative name of the configuration * * @author Klaus Meffert * @since 2.3 */ public String getName() { return m_name; } /** * Sets the fitness function to be used for this genetic algorithm. * The fitness function is responsible for evaluating a given Chromosome and * returning a positive integer that represents its worth as a candidate * solution. These values are used as a guide by the natural to determine * which Chromosome instances will be allowed to move on to the next round of * evolution, and which will instead be eliminated. * * Note that it is illegal to set both this fitness function and a bulk * fitness function. Although one or the other must be set, the two are * mutually exclusive. * * @param a_functionToSet fitness function to be used * * @throws InvalidConfigurationException if the fitness function is null, a * bulk fitness function has already been set, or if this Configuration * object is locked. * * @author Neil Rotstan * @since 1.1 */ public synchronized void setFitnessFunction(final FitnessFunction a_functionToSet) throws InvalidConfigurationException { verifyChangesAllowed(); // Sanity check: Make sure that the given fitness function isn't null. // ------------------------------------------------------------------- if (a_functionToSet == null) { throw new InvalidConfigurationException("The FitnessFunction instance must not be null."); } // Make sure the bulk fitness function hasn't already been set. // ------------------------------------------------------------ if (m_bulkObjectiveFunction != null) { throw new InvalidConfigurationException( "The bulk fitness function and normal fitness function " + "may not both be set."); } // Ensure that no other fitness function has been set in a different // configuration object within the same thread! // ----------------------------------------------------------------- checkProperty(PROPERTY_FITFUNC_INST, a_functionToSet, m_objectiveFunction, "Fitness function has already been set differently."); m_objectiveFunction = a_functionToSet; } /** * Verifies that a property is not set. If not set, set it, otherwise throw * a RuntimeException with a_errmsg as text. * @param a_propname the property to check (the current thread will be * considered as a part of the property's name, too) * @param a_obj the object that should be set in charge of the property * @param a_oldObj the old object that is set until now. Not used yet * @param a_errmsg the error message to throw in case the property is already * set for the current thread * * @author Klaus Meffert * @since 3.0 */ protected void checkProperty(final String a_propname, final Object a_obj, final Object a_oldObj, final String a_errmsg) { String instanceHash = System.getProperty(threadKey + a_propname, null); String key = makeKey(a_obj); if (instanceHash == null || instanceHash.length() < 1) { System.setProperty(threadKey + a_propname, key); } else if (!instanceHash.equals(key)) { throw new RuntimeException( a_errmsg + "\nIf you want to set or construct" + " a configuration multiple times, please call" + " static method Configuration.reset() before" + " each setting!"); } } /** * @param a_obj the object to make a key for, must not be null * @return key produced for the object (hashCode() is used, so it should be * implemented properly!) * * @author Klaus Meffert * @since 3.0 */ protected String makeKey(final Object a_obj) { String key = String.valueOf(a_obj.hashCode()) + a_obj.getClass().getName(); return key; } /** * Retrieves the fitness function previously setup in this Configuration * object. * * @return the fitness function * * @author Neil Rotstan * @since 1.0 */ public synchronized FitnessFunction getFitnessFunction() { return m_objectiveFunction; } /** * Sets the bulk fitness function to be used for this genetic algorithm. * The bulk fitness function may be used to evaluate and assign fitness * values to the entire group of candidate Chromosomes in a single batch. * This can be useful in cases where it's difficult to assign fitness * values to a Chromosome in isolation from the other candidate * Chromosomes. * <p> * Note that it is illegal to set both a bulk fitness function and a * normal fitness function. Although one or the other is required, the * two are mutually exclusive. * * @param a_functionToSet bulk fitness function to be used * * @throws InvalidConfigurationException if the bulk fitness function is * null, the normal fitness function has already been set, or if this * Configuration object is locked * * @author Neil Rotstan * @author Klaus Meffert * @since 1.0 */ public synchronized void setBulkFitnessFunction(final BulkFitnessFunction a_functionToSet) throws InvalidConfigurationException { verifyChangesAllowed(); // Sanity check: Make sure that the given bulk fitness function // isn't null. // ------------------------------------------------------------ if (a_functionToSet == null) { throw new InvalidConfigurationException("The BulkFitnessFunction instance must not be null."); } // Make sure a normal fitness function hasn't already been set. // ------------------------------------------------------------ if (m_objectiveFunction != null) { throw new InvalidConfigurationException( "The bulk fitness function and normal fitness function " + "must not both be set."); } // Ensure that no other bulk fitness function has been set in a // different configuration object within the same thread! // ------------------------------------------------------------ checkProperty(PROPERTY_BFITFNC_INST, a_functionToSet, m_bulkObjectiveFunction, "Bulk fitness function has already been set differently."); m_bulkObjectiveFunction = a_functionToSet; } /** * Retrieves the bulk fitness function previously setup in this * Configuration object. * * @return the bulk fitness function * * @author Neil Rotstan * @since 1.0 */ public synchronized BulkFitnessFunction getBulkFitnessFunction() { return m_bulkObjectiveFunction; } /** * Sets the sample Chromosome that is to be used as a guide for the * construction of other Chromosomes. The Chromosome should be setup * with each gene represented by the desired concrete Gene implementation * for that gene position (locus). Anytime a new Chromosome is created, * it will be constructed with the same Gene setup as that provided in * this sample Chromosome. * * @param a_sampleChromosomeToSet Chromosome to be used as the sample * @throws InvalidConfigurationException if the given Chromosome is null or * this Configuration object is locked * * @author Neil Rotstan * @since 1.0 */ public void setSampleChromosome(final IChromosome a_sampleChromosomeToSet) throws InvalidConfigurationException { verifyChangesAllowed(); // Sanity check: Make sure that the given chromosome isn't null. // ----------------------------------------------------------- if (a_sampleChromosomeToSet == null) { throw new InvalidConfigurationException("The sample chromosome instance must not be null."); } if (a_sampleChromosomeToSet.getConfiguration() == null) { throw new InvalidConfigurationException("The sample chromosome's configuration must not be null."); } // Ensure that no other sample chromosome has been set in a // different configuration object within the same thread! // -------------------------------------------------------- checkProperty(PROPERTY_SAMPLE_CHROM_INST, a_sampleChromosomeToSet, m_sampleChromosome, "Sample chromosome has already been set differently."); m_sampleChromosome = a_sampleChromosomeToSet; m_chromosomeSize = m_sampleChromosome.size(); } /** * Retrieves the sample Chromosome that contains the desired Gene setup * for each respective gene position (locus). * * @return sample Chromosome instance * * @author Neil Rotstan * @since 1.0 */ public IChromosome getSampleChromosome() { return m_sampleChromosome; } /** * Retrieves the chromosome size being used by this genetic * algorithm. This value is set automatically when the sample chromosome * is provided. * * @return chromosome size used in this configuration * * @author Neil Rotstan * @since 1.0 */ public int getChromosomeSize() { return m_chromosomeSize; } /** * Sets the natural selector to be used for this genetic algorithm. * The natural selector is responsible for actually selecting * which Chromosome instances are allowed to move on to the next * round of evolution (usually guided by the fitness values * provided by the fitness function). This setting is required. * * @param a_selectorToSet the natural selector to be used * * @throws InvalidConfigurationException if the natural selector is null or * this Configuration object is locked * * @author Neil Rotstan * @since 1.0 * @deprecated use addNaturalSelector(false) instead */ public synchronized void setNaturalSelector(final NaturalSelector a_selectorToSet) throws InvalidConfigurationException { addNaturalSelector(a_selectorToSet, false); } /** * Retrieves the natural selector setup in this Configuration instance. * * @return the natural selector * * @author Neil Rotstan * @since 1.0 * @deprecated use getNaturalSelectors(true) or getNaturalSelectors(false) * to obtain the relevant chain of NaturalSelector's and then call the * chain's get(index) method */ public synchronized NaturalSelector getNaturalSelector() { if (getNaturalSelectors(false).size() < 1) { return null; } return getNaturalSelectors(false).get(0); } /** * @param a_processBeforeGeneticOperators true: retrieve selector that is * registered to be executed before genetic operators, false: get the one * that is registered to be executed after genetic operators * @param a_index index of the selector to get * @return NaturalSelector * * @author Klaus Meffert * @since 1.1 */ public synchronized NaturalSelector getNaturalSelector(final boolean a_processBeforeGeneticOperators, final int a_index) { if (a_processBeforeGeneticOperators) { return m_preSelectors.get(a_index); } else { return m_postSelectors.get(a_index); } } /** * Only use for read-only access! Especially don't call clear() for the * returned ChainOfSelectors object! * * @param a_processBeforeGeneticOperators true: retrieve selector that is * registered to be executed before genetic operators, false: get the one * that is registered to be executed after genetic operators * @return ChainOfSelectors * * @author Klaus Meffert * @since 1.1 */ public ChainOfSelectors getNaturalSelectors(final boolean a_processBeforeGeneticOperators) { if (a_processBeforeGeneticOperators) { return m_preSelectors; } else { return m_postSelectors; } } /** * @param a_processBeforeGeneticOperators true: retrieve selector that is * registered to be executed before genetic operators, false: get the one * that is registered to be executed after genetic operators * @return size of selector list (distinct by a_processBeforeGeneticOperators) * * @author Klaus Meffert * @since 1.1 */ public int getNaturalSelectorsSize(final boolean a_processBeforeGeneticOperators) { if (a_processBeforeGeneticOperators) { return m_preSelectors.size(); } else { return m_postSelectors.size(); } } /** * Removes all natural selectors (either pre or post ones). * * @param a_processBeforeGeneticOperators true: remove all selectors * processed before genetic operators, false: remove the ones processed * afterwards * * @author Klaus Meffert * @since 2.3 */ public void removeNaturalSelectors(final boolean a_processBeforeGeneticOperators) { if (a_processBeforeGeneticOperators) { getNaturalSelectors(true).clear(); } else { getNaturalSelectors(false).clear(); } } /** * Sets the random generator to be used for this genetic algorithm. * The random generator is responsible for generating random numbers, * which are used throughout the process of genetic evolution and * selection. This setting is required. * * @param a_generatorToSet random generator to be used * * @throws InvalidConfigurationException if the random generator is null or * this object is locked * * @author Neil Rotstan * @since 1.0 */ public synchronized void setRandomGenerator(final RandomGenerator a_generatorToSet) throws InvalidConfigurationException { verifyChangesAllowed(); // Sanity check: Make sure that the given random generator isn't null. // ------------------------------------------------------------------- if (a_generatorToSet == null) { throw new InvalidConfigurationException("The RandomGenerator instance may not be null."); } m_randomGenerator = a_generatorToSet; } /** * Retrieves the random generator setup in this Configuration instance. * * @return the random generator * * @author Neil Rotstan * @since 1.0 */ public synchronized RandomGenerator getRandomGenerator() { return m_randomGenerator; } /** * Adds a genetic operator for use in this algorithm. Genetic operators * represent evolutionary steps that, when combined, make up the * evolutionary process. Examples of genetic operators are reproduction, * crossover, and mutation. During the evolution process, all of the * genetic operators added via this method are invoked in the order * they were added. At least one genetic operator must be provided. * * @param a_operatorToAdd the genetic operator to add. * * @throws InvalidConfigurationException if the genetic operator is null or * if this Configuration object is locked * * @author Neil Rotstan * @since 1.0 */ public synchronized void addGeneticOperator(final GeneticOperator a_operatorToAdd) throws InvalidConfigurationException { verifyChangesAllowed(); // Sanity check: Make sure that the given genetic operator isn't null. // ------------------------------------------------------------------- if (a_operatorToAdd == null) { throw new InvalidConfigurationException("The GeneticOperator instance must not be null."); } m_geneticOperators.add(a_operatorToAdd); } /** * Retrieves the genetic operators setup in this Configuration instance. * Note that once this Configuration instance is locked, a new, immutable list * of operators is used and any lists previously retrieved with this method * will no longer reflect the actual list in use. * * @return the list of genetic operators * * @author Neil Rotstan * @since 1.0 */ public List getGeneticOperators() { return m_geneticOperators; } /** * Sets the population size to be used for this genetic algorithm. * The population size is a fixed value that represents the * number of Chromosomes contained within the Genotype (population). * This setting is required. * * @param a_sizeOfPopulation population size to be used * * @throws InvalidConfigurationException if the population size is not * positive or this object is locked * * @author Neil Rotstan * @since 1.0 */ public synchronized void setPopulationSize(final int a_sizeOfPopulation) throws InvalidConfigurationException { verifyChangesAllowed(); // Sanity check: Make sure the population size is positive. // -------------------------------------------------------- if (a_sizeOfPopulation < 1) { throw new InvalidConfigurationException("The population size must be positive."); } m_config.m_populationSize = a_sizeOfPopulation; } /** * Retrieves the population size setup in this Configuration instance. * * @return population size */ public synchronized int getPopulationSize() { return m_config.m_populationSize; } /** * Sets the event manager that is to be associated with this configuration. * The event manager is responsible for the management of event subscribers * and event notifications. * * @param a_eventManagerToSet the EventManager instance to use in this * configuration * * @throws InvalidConfigurationException if the event manager is null * or this Configuration object is locked * * @author Neil Rotstan * @since 1.0 */ public void setEventManager(final IEventManager a_eventManagerToSet) throws InvalidConfigurationException { verifyChangesAllowed(); // Sanity check: Make sure that the given event manager isn't null. // ---------------------------------------------------------------- if (a_eventManagerToSet == null) { throw new InvalidConfigurationException("The event manager instance must not be null."); } // Ensure that no other event manager has been set in a different // configuration object within the same thread! // -------------------------------------------------------------- checkProperty(PROPERTY_EVENT_MGR_INST, a_eventManagerToSet, m_eventManager, "Event manager has already been set differently."); m_eventManager = a_eventManagerToSet; } /** * Retrieves the event manager associated with this configuration. The event * manager is responsible for the management of event subscribers * and event notifications. * * @return the actively configured event manager instance * * @since 1.0 */ public IEventManager getEventManager() { return m_eventManager; } /** * Sets the ChromosomePool that is to be associated with this * configuration. The ChromosomePool is used to pool discarded Chromosome * instances so that they may be recycled later, thereby saving memory and * the time to construct them from scratch. The presence of a ChromosomePool * is optional. If none exists, then a new Chromosome will be constructed * each time one is needed. * * @param a_chromosomePoolToSet the ChromosomePool instance to use * @throws InvalidConfigurationException if this object is locked * * @author Neil Rotstan * @since 1.0 */ public void setChromosomePool(final IChromosomePool a_chromosomePoolToSet) throws InvalidConfigurationException { verifyChangesAllowed(); m_chromosomePool = a_chromosomePoolToSet; } /** * Retrieves the ChromosomePool instance, if any, that is associated with * this configuration. The ChromosomePool is used to pool discarded * Chromosome instances so that they may be recycled later, thereby * saving memory and the time to construct them from scratch. The presence * of a ChromosomePool instance is optional. If none exists, then new * Chromosomes should be constructed each time one is needed. * * @return the ChromosomePool instance associated this configuration, or * null if none has been provided * * @author Neil Rotstan * @since 1.0 */ public IChromosomePool getChromosomePool() { return m_chromosomePool; } /** * Locks all of the settings in this configuration object. Once * this method is successfully invoked, none of the settings may * be changed. There is no way to unlock this object once it is locked. * <p> * Prior to returning successfully, this method will first invoke the * verifyStateIsValid() method to make sure that any required configuration * options have been properly set. If it detects a problem, it will * throw an InvalidConfigurationException and leave the object unlocked. * <p> * It's possible to test whether this object is locked through the * isLocked() method. * <p> * It is ok to lock an object more than once. In that case, this method * does nothing and simply returns. * * @throws InvalidConfigurationException if this Configuration object is * in an invalid state at the time of invocation * * @author Neil Rotstan * @since 1.0 */ public synchronized void lockSettings() throws InvalidConfigurationException { if (!m_settingsLocked) { verifyStateIsValid(); // // Make genetic operators list immutable. // // -------------------------------------- // m_geneticOperators = Collections.unmodifiableList(m_geneticOperators); m_settingsLocked = true; } } /** * Retrieves the lock status of this object. * * @return true if this object has been locked by a previous successful * call to the lockSettings() method, false otherwise * * @author Neil Rotstan * @since 1.0 */ public boolean isLocked() { return m_settingsLocked; } /** * Tests the state of this Configuration object to make sure it's valid. * This generally consists of verifying that required settings have, in * fact, been set. If this object is not in a valid state, then an * exception will be thrown detailing the reason the state is not valid. * * @throws InvalidConfigurationException if the state of this Configuration * is not valid. The error message in the exception will detail the reason * for invalidity * * @author Neil Rotstan * @since 1.0 */ public synchronized void verifyStateIsValid() throws InvalidConfigurationException { // First, make sure all of the required fields have been set to // appropriate values. // ------------------------------------------------------------ if (m_objectiveFunction == null && m_bulkObjectiveFunction == null) { throw new InvalidConfigurationException("A desired fitness function or bulk fitness function must " + "be specified in the active configuration."); } if (m_sampleChromosome == null) { throw new InvalidConfigurationException("A sample instance of the desired Chromosome " + "setup must be specified in the active configuration."); } if (m_preSelectors.size() == 0 && m_postSelectors.size() == 0) { throw new InvalidConfigurationException( "At least one desired natural selector must be specified in the" + " active configuration."); } if (m_randomGenerator == null) { throw new InvalidConfigurationException( "A desired random number generator must be specified in the " + "active configuration."); } if (m_eventManager == null) { throw new InvalidConfigurationException( "A desired event manager must be specified in the active " + "configuration."); } if (m_geneticOperators.isEmpty()) { throw new InvalidConfigurationException( "At least one genetic operator must be specified in the " + "configuration."); } if (m_chromosomeSize <= 0) { throw new InvalidConfigurationException( "A chromosome size greater than zero must be specified in " + "the active configuration."); } if (m_config.m_populationSize <= 0) { throw new InvalidConfigurationException( "A population size greater than zero must be specified in " + "the active configuration."); } if (m_fitnessEvaluator == null) { throw new IllegalArgumentException("The fitness evaluator must not be null."); } // Next, it's critical that each Gene implementation in the sample // Chromosome has a working equals() method, or else the genetic // engine will end up failing in mysterious and unpredictable ways. // We therefore verify right here that this method is working properly // in each of the Gene implementations used in the sample Chromosome. // ------------------------------------------------------------------- Gene[] sampleGenes = m_sampleChromosome.getGenes(); for (int i = 0; i < sampleGenes.length; i++) { Gene sampleCopy = sampleGenes[i].newGene(); sampleCopy.setAllele(sampleGenes[i].getAllele()); if (!(sampleCopy.equals(sampleGenes[i]))) { throw new InvalidConfigurationException("The sample Gene at gene position (locus) " + i + " does not appear to have a working equals() or compareTo()" + " method.\n" + "It could also be that you forgot to implement method" + " newGene() in your Gene implementation.\n" + "When tested, the method returned false when comparing " + "the sample gene with a gene of the same type and " + "possessing the same value (allele)."); } } } /** * Makes sure that this Configuration object isn't locked. If it is, then * an exception is thrown with an appropriate message indicating * that settings in this object may not be altered. This method * should be invoked by any mutator method in this object prior * to making any state alterations. * * @throws InvalidConfigurationException if this Configuration object is * locked * * @author Neil Rotstan * @since 1.0 */ protected void verifyChangesAllowed() throws InvalidConfigurationException { if (m_settingsLocked) { throw new InvalidConfigurationException( "This Configuration object is locked. Settings may not be " + "altered."); } } /** * Adds a NaturalSelector to the ordered chain of registered * NaturalSelector's. It's possible to execute the NaturalSelector before * or after (registered) genetic operations have been applied. * Normally, you would add a selector that is applied after the genetic * operators are processed (a_processBeforeGeneticOperators = false). * * @param a_selector the selector to be added to the chain * @param a_processBeforeGeneticOperators true: execute NaturalSelector * before any genetic operator will be applied, false: .. after.. * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 1.1 */ public void addNaturalSelector(NaturalSelector a_selector, boolean a_processBeforeGeneticOperators) throws InvalidConfigurationException { verifyChangesAllowed(); if (a_processBeforeGeneticOperators) { m_preSelectors.addNaturalSelector(a_selector); } else { m_postSelectors.addNaturalSelector(a_selector); } } /** * Minimum size guaranteed for population. This is significant during * evolution as natural selectors could select fewer chromosomes for the next * generation than the initial population size was. * @param a_minimumSizeGuaranteedPercent if zero or below then no ensurance * for size given, see Genotype.evolve() * * @author Klaus Meffert */ public void setMinimumPopSizePercent(int a_minimumSizeGuaranteedPercent) { m_minPercentageSizePopulation = a_minimumSizeGuaranteedPercent; } public int getMinimumPopSizePercent() { return m_minPercentageSizePopulation; } /** * @return the assigned FitnessEvaluator * * @author Klaus Meffert * @since 2.0 */ public FitnessEvaluator getFitnessEvaluator() { return m_fitnessEvaluator; } /** * Set the fitness evaluator (deciding if a given fitness value is better * when it's higher or better when it's lower). * @param a_fitnessEvaluator the FitnessEvaluator to be used * * @author Klaus Meffert * @since 2.0 */ public void setFitnessEvaluator(FitnessEvaluator a_fitnessEvaluator) { if (a_fitnessEvaluator == null) { throw new IllegalStateException("The fitness evaluator object must not be null!"); } // Ensure that no other fitness evaluator has been set in a // different configuration object within the same thread! // -------------------------------------------------------- checkProperty(PROPERTY_FITEVAL_INST, a_fitnessEvaluator, m_fitnessEvaluator, "Fitness evaluator has already been set differently."); m_fitnessEvaluator = a_fitnessEvaluator; } /** * @return true: fittest chromosome should always be transferred to next * generation * * @author Klaus Meffert * @since 2.1 */ public boolean isPreserveFittestIndividual() { return m_preserveFittestIndividual; } /** * Determines whether to save (keep) the fittest individual. * @param a_preserveFittest true: always transfer fittest chromosome to next * generation * * @author Klaus Meffert * @since 2.1 */ public void setPreservFittestIndividual(boolean a_preserveFittest) { m_preserveFittestIndividual = a_preserveFittest; } public void incrementGenerationNr() { m_generationNr++; } public int getGenerationNr() { return m_generationNr; } /** * Implementation of the Configurable interface. * @return ConfigurationHandler * @throws ConfigException * * @author Siddhartha Azad */ public ConfigurationHandler getConfigurationHandler() { return m_conHandler; } /** * @return string representation of the configuration containing all * configurable elements * * @author Klaus Meffert * @since 2.3 */ public String toString() { String result = S_CONFIGURATION + ":"; // Basic parameters. // ----------------- result += "\n " + S_CONFIGURATION_NAME + ": " + getName(); result += "\n " + S_POPULATION_SIZE + ": " + getPopulationSize(); result += "\n " + S_MINPOPSIZE + ": " + getMinimumPopSizePercent(); result += "\n " + S_CHROMOSOME_SIZE + ": " + getChromosomeSize(); // Sample chromosome. // ------------------ result += "\n " + S_SAMPLE_CHROM + ":\n"; if (getSampleChromosome() == null) { result += "\n null"; } else { result += "\n " + S_SIZE + ": " + getSampleChromosome().size(); result += "\n " + S_TOSTRING + ": " + getSampleChromosome().toString(); } // Random generator. // ----------------- result += "\n " + S_RANDOM_GENERATOR + ": "; if (getRandomGenerator() != null) { result += getRandomGenerator().getClass().getName(); } else { result += S_NONE; } result += "\n " + S_EVENT_MANAGER + ": "; // Event manager. // -------------- if (getEventManager() == null) { result += S_NONE; } else { result += getEventManager().getClass().getName(); } // Configuration handler. // ---------------------- result += "\n " + S_CONFIGURATION_HANDLER + ": "; if (getConfigurationHandler() == null) { result += "null"; } else { result += getConfigurationHandler().getName(); } // Fitness function. // ----------------- result += "\n " + S_FITNESS_FUNCTION + ": "; if (getFitnessFunction() == null) { result += "null"; } else { result += getFitnessFunction().getClass().getName(); } // Fitness evaluator. // ------------------ result += "\n " + S_FITNESS_EVALUATOR + ": "; if (getFitnessEvaluator() == null) { result += "null"; } else { result += getFitnessEvaluator().getClass().getName(); } // Genetic operators. // ------------------ result += "\n " + S_GENETIC_OPERATORS + ": "; if (getGeneticOperators() == null) { result += "null"; } else { int gensize = getGeneticOperators().size(); if (gensize < 1) { result += S_NONE; } else { for (int i = 0; i < gensize; i++) { if (i > 0) { result += "; "; } result += " " + getGeneticOperators().get(i).getClass().getName(); } } } // Natural selectors (pre). // ------------------------ int natsize = getNaturalSelectors(true).size(); result += "\n " + S_NATURAL_SELECTORS + "(" + S_PRE + "): "; if (natsize < 1) { result += S_NONE; } else { for (int i = 0; i < natsize; i++) { if (i > 0) { result += "; "; } result += " " + getNaturalSelectors(true).get(i).getClass().getName(); } } // Natural selectors (post). // ------------------------- natsize = getNaturalSelectors(false).size(); result += "\n " + S_NATURAL_SELECTORS + "(" + S_POST + "): "; if (natsize < 1) { result += "none"; } else { for (int i = 0; i < natsize; i++) { if (i > 0) { result += "; "; } result += " " + getNaturalSelectors(false).get(i).getClass().getName(); } } // String popSelector; // if (m_popConstantSelector == null) { // popSelector = "null"; // } // else { // popSelector = m_popConstantSelector.getClass().getName(); // } // result += "\n " + S_POPCONSTANT_SELECTOR + ": " // + popSelector; return result; } /** * See setKeepPopulationSizeConstant and * GABreeder#evolve(Population, Configuration) for detailled explanation. * @return true: population size will always be the same size * (as given with Configuration.setPopulationSize(int) * * @author Klaus Meffert * @since 2.4 */ public boolean isKeepPopulationSizeConstant() { return m_keepPopulationSizeConstant; } /** * Allows to keep the population size constant during one evolution, even if * there is no appropriate instance of NaturalSelector (such as * WeightedRouletteSelector) registered with the Configuration.<p> * See setKeepPopulationSizeConstant and * GABreeder#evolve(Population, Configuration) for detailled explanation. * * @param a_keepPopSizeConstant true: population size will always be * the same size (as given with Configuration.setPopulationSize(int) * * @author Klaus Meffert * @since 2.4 */ public void setKeepPopulationSizeConstant(boolean a_keepPopSizeConstant) { m_keepPopulationSizeConstant = a_keepPopSizeConstant; } public void setSelectFromPrevGen(double a_percentage) { if (a_percentage < 0 || a_percentage > 1.00) { throw new IllegalArgumentException("Argument must be between 0 and 1"); } m_selectFromPrevGen = a_percentage; } public double getSelectFromPrevGen() { return m_selectFromPrevGen; } /** * @return the JGAP factory registered * * @author Klaus Meffert */ public IJGAPFactory getJGAPFactory() { return m_factory; } public class ConfigurationConfigurable implements Serializable { /** * The number of chromosomes that will be stored in the Genotype. */ int m_populationSize; } /** * Builds a string considering the current thread and the given id. * * @param current the current Thread * @param a_id a hopefully unique id of the configuration * * @return string built up * * @author Klaus Meffert * @since 3.01 */ protected static String getThreadKey(Thread current, String a_id) { return current.toString() + "|" + a_id + "|"; } /** * @param a_factory the IJGAPFactory to use * * @author Klaus Meffert * @since 3.01 */ public void setJGAPFactory(IJGAPFactory a_factory) { m_factory = a_factory; } /** * @param a_breeder the breeder to use * * @author Klaus Meffert * @since 3.2 */ public void setBreeder(IBreeder a_breeder) { assert (a_breeder != null); m_breeder = a_breeder; } /** * @return the breeder set or new standard breeder instance in case no breeder * was set before. * * @author Klaus Meffert * @since 3.2 */ public IBreeder getBreeder() { if (m_breeder == null) { m_breeder = new GABreeder(); } return m_breeder; } // /** // * If population size should be kept constant then this selector determines // * which of the chromosomes to select into the next generation. // * // * @param a_popConstantSelector the selector to use // * @throws InvalidConfigurationException // * // * @author Klaus Meffert // * @since 3.2.2 // */ // public void setKeepPopConstantSelector(INaturalSelector a_popConstantSelector) // throws InvalidConfigurationException { // verifyChangesAllowed(); // m_popConstantSelector = a_popConstantSelector; // } // /** // * @return the registered selector to keep population size constant. If none // * is set, the NewestChromosomesSelector is instantiated, assigned and // * returned // * // * @throws InvalidConfigurationException // * // * @author Klaus Meffert // * @since 3.2.2 // */ // public INaturalSelector getKeepPopConstantSelector() // throws InvalidConfigurationException{ // if (m_popConstantSelector == null) { // m_popConstantSelector = new NewestChromosomesSelector(this); // } // return m_popConstantSelector; // } /** * @param a_alwaysCalculate true: Chromosome.getFitnessValue() will always * (re-)calculate the fitness value. This may be necessary in case of * environments where the state changes without the chromosome to notice. * * @author Klaus Meffert * @since 3.2.2 */ public void setAlwaysCaculateFitness(boolean a_alwaysCalculate) { m_alwaysCalculateFitness = a_alwaysCalculate; } /** * @return true: Chromosome.getFitnessValue() will always (re-)calculate the * fitness value. This may be necessary in case of environments where the * state changes without the chromosome to notice. * * @author Klaus Meffert * @since 3.2.2 */ public boolean isAlwaysCalculateFitness() { return m_alwaysCalculateFitness; } protected String makeThreadKey() { Thread current = Thread.currentThread(); threadKey = getThreadKey(current, m_id); return threadKey; } /** * Sets a property in the bag. * * Be aware that setting in unserializable value here leads to problems with * distributed computing or other scenarios based on serialization! * * Be aware that cloning may also lead to conflicts * in case you use unsupported object types! * * @param a_propName name of the property to se the value for * @param a_value value of the property * * @author Klaus Meffert * @since 3.3.1 */ // public void setPropertyInBag(String a_propName, Object a_value) { // m_propertyBag.put(a_propName, a_value); // } /** * * @param a_propName name of the property to retrieve the value from * @return value of the property * * @author Klaus Meffert * @since 3.3.1 */ // public Object getPropertyFromBag(String a_propName) { // return m_propertyBag.get(a_propName); // } /** * Deserialize the object. Needed to provide a unique ID for each thread the * object is used in. * * @param a_inputStream the ObjectInputStream provided for deserialzation * @throws ClassNotFoundException * @throws IOException * * @author Klaus Meffert * @since 3.01 */ private void readObject(ObjectInputStream a_inputStream) throws ClassNotFoundException, IOException { // Always perform the default de-serialization first. // -------------------------------------------------- a_inputStream.defaultReadObject(); makeThreadKey(); } /** * @return the id of the configuration set * * @author Klaus Meffert * @since 3.1 */ public String getId() { return m_id; } /** * Only to be called by sub classes, such as GPConfiguration. * * @param a_id the id to set * * @author Klaus Meffert * @since 3.2 */ protected void setId(String a_id) { m_id = a_id; } /** * @return deep clone of this instance * * @author Klaus Meffert * @since 3.2 */ public Object clone() { return newInstance(m_id, m_name); } /** * Creates a new Configuration instance by cloning. Allows to preset the * ID and the name. * * @param a_id new ID for clone * @param a_name new name for clone * @return deep clone of this instance * * @author Klaus Meffert * @since 3.2 */ public Configuration newInstance(String a_id, String a_name) { try { Configuration result = new Configuration(m_name); // Clone JGAPFactory first because it helps in cloning other objects. // ------------------------------------------------------------------ if (m_factory instanceof ICloneable) { result.m_factory = (IJGAPFactory) ((ICloneable) m_factory).clone(); } else { // We must fallback to a standardized solution. // -------------------------------------------- m_factory = new JGAPFactory(false); result.m_factory = (IJGAPFactory) ((JGAPFactory) m_factory).clone(); } if (m_breeder != null) { result.m_breeder = (IBreeder) doClone(m_breeder); } if (m_bulkObjectiveFunction != null) { result.m_bulkObjectiveFunction = m_bulkObjectiveFunction; } result.m_chromosomeSize = m_chromosomeSize; // result.m_chromosomePool = m_chromosomePool.clone(); // result.m_conHandler = m_conHandler.clone(); result.m_eventManager = (IEventManager) doClone(m_eventManager); result.m_fitnessEvaluator = (FitnessEvaluator) doClone(m_fitnessEvaluator); result.m_generationNr = 0; result.m_geneticOperators = (List) doClone(m_geneticOperators); result.m_keepPopulationSizeConstant = m_keepPopulationSizeConstant; result.m_minPercentageSizePopulation = m_minPercentageSizePopulation; result.m_selectFromPrevGen = m_selectFromPrevGen; result.m_objectiveFunction = (FitnessFunction) doClone(m_objectiveFunction); // result.m_popConstantSelector = (INaturalSelector)doClone(m_popConstantSelector); result.m_postSelectors = (ChainOfSelectors) doClone(m_postSelectors); result.m_preSelectors = (ChainOfSelectors) doClone(m_preSelectors); result.m_preserveFittestIndividual = m_preserveFittestIndividual; result.m_randomGenerator = (RandomGenerator) doClone(m_randomGenerator); if (m_sampleChromosome != null) { result.m_sampleChromosome = (IChromosome) m_sampleChromosome.clone(); } result.m_alwaysCalculateFitness = m_alwaysCalculateFitness; result.m_settingsLocked = m_settingsLocked; // result.m_propertyBag = (Map)doClone(m_propertyBag); // Configurable data. // ------------------ result.m_config = new ConfigurationConfigurable(); result.m_config.m_populationSize = m_config.m_populationSize; // Identificative data. // -------------------- result.m_name = a_name; result.m_id = a_id; result.makeThreadKey();// Must be called after m_id is set return result; } catch (Throwable t) { throw new CloneException(t); } } /** * Helper called from clone. * * @param a_objToClone the object to clone * @return cloned object or null, if cloning not possible * @throws Exception * * @author Klaus Meffert * @since 3.2 */ protected Object doClone(Object a_objToClone) throws Exception { if (a_objToClone != null) { ICloneHandler handler = getJGAPFactory().getCloneHandlerFor(a_objToClone, null); if (handler != null) { return handler.perform(a_objToClone, null, null); } else { /**@todo try cloning in a standard way*/ } } return null; } /** * The equals-method. * * @param a_other the other object to compare * @return true if seen as equal * * @author Klaus Meffert * @since 3.2 */ public boolean equals(Object a_other) { return compareTo(a_other) == 0; } /** * The compareTo-method. * * @param a_other the other object to compare * @return -1, 0, 1 * * @author Klaus Meffert * @since 3.2 */ public int compareTo(Object a_other) { if (a_other == null) { return 1; } else { Configuration other = (Configuration) a_other; try { return new CompareToBuilder().append(m_config.m_populationSize, other.m_config.m_populationSize) .append(m_factory, other.m_factory).append(m_breeder, other.m_breeder) .append(m_objectiveFunction, other.m_objectiveFunction) .append(m_fitnessEvaluator, other.m_fitnessEvaluator) .append(m_bulkObjectiveFunction, other.m_bulkObjectiveFunction) .append(m_sampleChromosome, other.m_sampleChromosome) .append(m_randomGenerator, other.m_randomGenerator) // .append(m_eventManager, other.m_eventManager) // .append(m_chromosomePool, other.m_chromosomePool) .append(m_geneticOperators.toArray(), other.m_geneticOperators.toArray()) .append(m_chromosomeSize, other.m_chromosomeSize) .append(m_preSelectors, other.m_preSelectors).append(m_postSelectors, other.m_postSelectors) // .append(m_popConstantSelector, other.m_popConstantSelector) .append(m_preserveFittestIndividual, other.m_preserveFittestIndividual) // .append(m_conHandler, other.m_conHandler) .append(threadKey, other.threadKey) .append(m_keepPopulationSizeConstant, other.m_keepPopulationSizeConstant) .append(m_alwaysCalculateFitness, other.m_alwaysCalculateFitness) .append(m_minPercentageSizePopulation, other.m_minPercentageSizePopulation) .append(m_selectFromPrevGen, other.m_selectFromPrevGen) .append(m_generationNr, other.m_generationNr).append(m_name, other.m_name) // .append(m_propertyBag, other.m_propertyBag) .append(m_settingsLocked, other.m_settingsLocked).toComparison(); } catch (ClassCastException cex) { throw new RuntimeException( "Cannot compare all objects within" + " org.jgap.Configuration, because at" + " least one does not implement interface" + " java.lang.Comparable!"); } } } /** * Sets a monitor for auditing evolution progress. * * @param a_monitor the monitor to use * * @author Klaus Meffert * @since 3.5 */ public void setMonitor(IEvolutionMonitor a_monitor) { m_monitor = a_monitor; if (m_monitor != null) { setUniqueKeysActive(true); } } /** * @return the monitor uses for auditing the evolution progress. * * @author Klaus Meffert * @since 3.5 */ public IEvolutionMonitor getMonitor() { return m_monitor; } /** * @param a_active true: use unique keys to allow tracking and monitoring * * @author Klaus Meffert * @since 3.5 */ public void setUniqueKeysActive(boolean a_active) { m_uniqueKeysActive = a_active; } /** * @return true: use unique keys to allow tracking and monitoring * * @author Klaus Meffert * @since 3.5 */ public boolean isUniqueKeysActive() { return m_uniqueKeysActive; } }