KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jgap > Chromosome


1 /**
2  * JGAP offers a dual license model(see below for specific license information):
3  * + The LGPL may be used anytime.
4  * + The MPL may be used if at least $20 have been donated to the JGAP project
5  * thru PayPal (see http://www.sourceforge.net/projects/jgap or, directly,
6  * http://sourceforge.net/donate/index.php?group_id=11618).
7  * Details about usage of JGAP under the MPL can be found at the homepage
8  * http://jgap.sourceforge.net/.
9  *
10  * Specific license information (MPL and LGPL)
11  * -------------------------------------------
12  * The contents of this file are subject to the Mozilla Public License Version
13  * 1.1 (the "License"); you may not use this file except in compliance with the
14  * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
15  *
16  * Software distributed under the License is distributed on an "AS IS" basis,
17  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
18  * the specific language governing rights and limitations under the License.
19  *
20  * The Original Code is 'JGAP - Java Genetic Algorithms Package'.
21  * The Initial Developer of the Original Code is Neil Rotstan. Portions created
22  * by the Initial Developer are Copyright (C) 2002- 2003 by Neil Rotstan.
23  * All Rights Reserved.
24  * Co-developer of the code is Klaus Meffert. Portions created by the co-
25  * developer are Copyright (C) 2003-2006 by Klaus Meffert. All Rights Reserved.
26  * Contributor(s): all the names of the contributors are added in the source
27  * code where applicable.
28  *
29  * Alternatively, the contents of this file may be used under the terms of the
30  * LGPL license (the "GNU LESSER PUBLIC LICENSE"), in which case the
31  * provisions of LGPL are applicable instead of those above. If you wish to
32  * allow use of your version of this file only under the terms of the LGPL
33  * License and not to allow others to use your version of this file under
34  * the MPL, indicate your decision by deleting the provisions above and
35  * replace them with the notice and other provisions required by the LGPL.
36  * If you do not delete the provisions above, a recipient may use your version
37  * of this file under either the MPL or the LGPL.
38  *
39  * This library is free software; you can redistribute it and/or modify it
40  * under the terms of the MPL as stated above or under the terms of the LGPL.
41  * This library is distributed in the hope that it will be useful, but WITHOUT
42  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
43  * FOR A PARTICULAR PURPOSE. See the GNU Lesser Public License for more
44  * details.
45  */

46 package org.jgap;
47
48 import java.lang.reflect.*;
49 import java.util.*;
50 import java.io.*;
51 import java.net.*;
52
53 /**
54  * Chromosomes represent potential solutions and consist of a fixed-length
55  * collection of genes. Each gene represents a discrete part of the solution.
56  * Each gene in the Chromosome may be backed by a different concrete
57  * implementation of the Gene interface, but all genes in a respective
58  * position (locus) must share the same concrete implementation across
59  * Chromosomes within a single population (genotype). In other words, gene 1
60  * in a chromosome must share the same concrete implementation as gene 1 in all
61  * other chromosomes in the population.
62  *
63  * @author Neil Rotstan
64  * @author Klaus Meffert
65  * @since 1.0
66  */

67 public class Chromosome
68     extends BaseChromosome implements IPersistentRepresentation {
69   /** String containing the CVS revision. Read out via reflection!*/
70   private final static String JavaDoc CVS_REVISION = "$Revision: 1.91 $";
71   /**
72    * Application-specific data that is attached to this Chromosome.
73    * This data may assist the application in evaluating this Chromosome
74    * in the fitness function. JGAP does not operate on the data, aside
75    * from allowing it to be set and retrieved, and considering it with
76    * comparations (if user opted in to do so).
77    */

78   private Object JavaDoc m_applicationData;
79
80   /**
81    * Holds multiobjective values.
82    *
83    * @since 2.6
84    * @todo move to new subclass of Chromosome (and introduce new interface
85    * IMultiObjective with that)
86    */

87   private List m_multiObjective;
88
89   /**
90    * The array of Genes contained in this Chromosome.
91    */

92   private Gene[] m_genes;
93
94   /**
95    * Keeps track of whether or not this Chromosome has been selected by
96    * the natural selector to move on to the next generation.
97    */

98   private boolean m_isSelectedForNextGeneration;
99
100   /**
101    * Stores the fitness value of this Chromosome as determined by the
102    * active fitness function. A value of -1 indicates that this field
103    * has not yet been set with this Chromosome's fitness values (valid
104    * fitness values are always positive).
105    *
106    * @since 2.0 (until 1.1: type int)
107    */

108   protected double m_fitnessValue = FitnessFunction.NO_FITNESS_VALUE;
109
110   /**
111    * Method compareTo(): Should we also consider the application data when
112    * comparing? Default is "false", as "true" means a Chromosome's losing its
113    * identity when application data is set differently!
114    *
115    * @since 2.2
116    */

117   private boolean m_compareAppData;
118
119   /**
120    * Optional helper class for checking if a given allele value to be set
121    * for a given gene is valid. If not, the allele value may not be set for the
122    * gene or the gene type (e.g. IntegerGene) is not allowed in general!
123    *
124    * @since 2.5
125    */

126   private IGeneConstraintChecker m_geneAlleleChecker;
127
128   /**
129    * This field separates gene class name from the gene persistent representation
130    * string. '*' does not work properly with URLEncoder!
131    */

132   public final static String JavaDoc GENE_DELIMITER = "#";
133
134   /**
135    * Represents the heading delimiter that is used to separate genes in the
136    * persistent representation of Chromosome instances.
137    */

138   public final static String JavaDoc GENE_DELIMITER_HEADING = "<";
139
140   /**
141    * Represents the closing delimiter that is used to separate genes in the
142    * persistent representation of Chromosome instances.
143    */

144   public final static String JavaDoc GENE_DELIMITER_CLOSING = ">";
145
146   /**
147    * Separates chromosome-related information.
148    */

149   public final static String JavaDoc CHROM_DELIMITER = "#";
150
151   /**
152    * Default constructor, only provided for dynamic instantiation.<p>
153    * Attention: The configuration used is the one set with the static method
154    * Genotype.setConfiguration.
155    *
156    * @throws InvalidConfigurationException
157    *
158    * @author Klaus Meffert
159    * @since 2.4
160    */

161   public Chromosome()
162       throws InvalidConfigurationException {
163     super(Genotype.getStaticConfiguration());
164   }
165
166   /**
167    * Constructor, provided for dynamic or minimal instantiation.
168    *
169    * @param a_configuration the configuration to use
170    * @throws InvalidConfigurationException
171    *
172    * @author Klaus Meffert
173    * @since 3.0
174    */

175   public Chromosome(final Configuration a_configuration)
176       throws InvalidConfigurationException {
177     super(a_configuration);
178   }
179
180   /**
181    * Constructor, provided for instantiation via persistent representation.
182    *
183    * @param a_configuration the configuration to use
184    * @param a_persistentRepresentatuion valid persistent representation that
185    * was most likely obtained via getPersistentRepresentation()
186    * @throws InvalidConfigurationException
187    * @throws UnsupportedRepresentationException
188    *
189    * @author Klaus Meffert
190    * @since 3.2
191    */

192   public Chromosome(final Configuration a_configuration,
193                     String JavaDoc a_persistentRepresentatuion)
194       throws InvalidConfigurationException, UnsupportedRepresentationException {
195     super(a_configuration);
196     setValueFromPersistentRepresentation(a_persistentRepresentatuion);
197   }
198
199   /**
200    * Constructor for specifying the number of genes.
201    *
202    * @param a_configuration the configuration to use
203    * @param a_desiredSize number of genes the chromosome contains of
204    * @throws InvalidConfigurationException
205    *
206    * @author Klaus Meffert
207    * @since 2.2
208    */

209   public Chromosome(final Configuration a_configuration,
210                     final int a_desiredSize)
211       throws InvalidConfigurationException {
212     super(a_configuration);
213     if (a_desiredSize <= 0) {
214       throw new IllegalArgumentException JavaDoc(
215           "Chromosome size must be greater than zero");
216     }
217     m_genes = new Gene[a_desiredSize];
218   }
219
220   /**
221    * Constructs a Chromosome of the given size separate from any specific
222    * Configuration. This constructor will use the given sample Gene to
223    * construct a new Chromosome instance containing genes all of the same
224    * type as the sample Gene. This can be useful for constructing sample
225    * chromosomes that use the same Gene type for all of their genes and that
226    * are to be used to setup a Configuration object.
227    *
228    * @param a_configuration the configuration to use
229    * @param a_sampleGene a concrete sampleGene instance that will be used
230    * as a template for all of the genes in this Chromosome
231    * @param a_desiredSize the desired size (number of genes) of this Chromosome
232    * @throws InvalidConfigurationException
233    *
234    * @author Neil Rotstan
235    * @author Klaus Meffert
236    * @since 1.0
237    */

238   public Chromosome(final Configuration a_configuration,
239                     final Gene a_sampleGene, final int a_desiredSize)
240       throws InvalidConfigurationException {
241     this(a_configuration, a_desiredSize);
242     initFromGene(a_sampleGene);
243   }
244
245   public Chromosome(final Configuration a_configuration, Gene a_sampleGene,
246                     int a_desiredSize,
247                     IGeneConstraintChecker a_constraintChecker)
248       throws InvalidConfigurationException {
249     this(a_configuration, a_desiredSize);
250     initFromGene(a_sampleGene);
251     setConstraintChecker(a_constraintChecker);
252   }
253
254   protected void initFromGene(Gene a_sampleGene) {
255     // Do sanity checking to make sure the parameters we were
256
// given are valid.
257
// ------------------------------------------------------
258
if (a_sampleGene == null) {
259       throw new IllegalArgumentException JavaDoc(
260           "Sample Gene cannot be null.");
261     }
262     // Populate the array of genes it with new Gene instances
263
// created from the sample gene.
264
// ------------------------------------------------------
265
for (int i = 0; i < m_genes.length; i++) {
266       m_genes[i] = a_sampleGene.newGene();
267     }
268   }
269
270   /**
271    * Constructs a Chromosome separate from any specific Configuration. This
272    * can be useful for constructing sample chromosomes that are to be used
273    * to setup a Configuration object.
274    *
275    * @param a_configuration the configuration to use
276    * @param a_initialGenes the initial genes of this Chromosome
277    * @throws InvalidConfigurationException
278    *
279    * @author Neil Rotstan
280    * @since 1.0
281    */

282   public Chromosome(final Configuration a_configuration, Gene[] a_initialGenes)
283       throws InvalidConfigurationException {
284     this(a_configuration, a_initialGenes == null ? 0 : a_initialGenes.length);
285     checkGenes(a_initialGenes);
286     m_genes = a_initialGenes;
287   }
288
289   /**
290    * Constructs a Chromosome separate from any specific Configuration. This
291    * can be useful for constructing sample chromosomes that are to be used
292    * to setup a Configuration object. Additionally, a constraint checker can be
293    * specified. It is used right here to verify the validity of the gene types
294    * supplied.
295    *
296    * @param a_configuration the configuration to use
297    * @param a_initialGenes the initial genes of this Chromosome
298    * @param a_constraintChecker constraint checker to use
299    * @throws InvalidConfigurationException in case the constraint checker
300    * reports a configuration error
301    *
302    * @author Klaus Meffert
303    * @since 2.5
304    */

305   public Chromosome(final Configuration a_configuration, Gene[] a_initialGenes,
306                     IGeneConstraintChecker a_constraintChecker)
307       throws InvalidConfigurationException {
308     this(a_configuration, a_initialGenes.length);
309     checkGenes(a_initialGenes);
310     m_genes = a_initialGenes;
311     setConstraintChecker(a_constraintChecker);
312   }
313
314   /**
315    * Helper: called by constructors only to verify the initial genes.
316    *
317    * @param a_initialGenes the initial genes of this Chromosome to verify
318    *
319    * @author Klaus Meffert
320    * @since 2.5
321    */

322   protected void checkGenes(Gene[] a_initialGenes) {
323     // Sanity checks: make sure the genes array isn't null and
324
// that none of the genes contained within it are null.
325
// Check against null already done in constructors!
326
// -------------------------------------------------------
327
for (int i = 0; i < a_initialGenes.length; i++) {
328       if (a_initialGenes[i] == null) {
329         throw new IllegalArgumentException JavaDoc(
330             "The gene at index " + i + " in the given array of " +
331             "genes was found to be null. No gene in the array " +
332             "may be null.");
333       }
334     }
335   }
336
337   /**
338    * Returns a copy of this Chromosome. The returned instance can evolve
339    * independently of this instance. Note that, if possible, this method
340    * will first attempt to acquire a Chromosome instance from the active
341    * ChromosomePool (if any) and set its value appropriately before
342    * returning it. If that is not possible, then a new Chromosome instance
343    * will be constructed and its value set appropriately before returning.
344    *
345    * @return copy of this Chromosome
346    * @throws IllegalStateException instead of CloneNotSupportedException
347    *
348    * @author Neil Rotstan
349    * @author Klaus Meffert
350    * @since 1.0
351    */

352   public synchronized Object JavaDoc clone() {
353     // Before doing anything, make sure that a Configuration object
354
// has been set on this Chromosome. If not, then throw an
355
// IllegalStateException.
356
// ------------------------------------------------------------
357
if (getConfiguration() == null) {
358       throw new IllegalStateException JavaDoc(
359           "The active Configuration object must be set on this " +
360           "Chromosome prior to invocation of the clone() method.");
361     }
362     IChromosome copy = null;
363     // Now, first see if we can pull a Chromosome from the pool and just
364
// set its gene values (alleles) appropriately.
365
// ------------------------------------------------------------
366
IChromosomePool pool = getConfiguration().getChromosomePool();
367     if (pool != null) {
368       copy = pool.acquireChromosome();
369       if (copy != null) {
370         Gene[] genes = copy.getGenes();
371         for (int i = 0; i < size(); i++) {
372           genes[i].setAllele(m_genes[i].getAllele());
373         }
374       }
375     }
376     try {
377       if (copy == null) {
378         // We couldn't fetch a Chromosome from the pool, so we need to create
379
// a new one. First we make a copy of each of the Genes. We explicity
380
// use the Gene at each respective gene location (locus) to create the
381
// new Gene that is to occupy that same locus in the new Chromosome.
382
// -------------------------------------------------------------------
383
int size = size();
384         if (size > 0) {
385           Gene[] copyOfGenes = new Gene[size];
386           for (int i = 0; i < copyOfGenes.length; i++) {
387             copyOfGenes[i] = m_genes[i].newGene();
388             copyOfGenes[i].setAllele(m_genes[i].getAllele());
389           }
390           // Now construct a new Chromosome with the copies of the genes and
391
// return it. Also clone the IApplicationData object.
392
// ---------------------------------------------------------------
393
/**@todo clone Config!*/
394           copy = new Chromosome(getConfiguration(), copyOfGenes);
395         }
396         else {
397           copy = new Chromosome(getConfiguration());
398         }
399       }
400       // Clone constraint checker.
401
// -------------------------
402
copy.setConstraintChecker(getConstraintChecker());
403     }
404     catch (InvalidConfigurationException iex) {
405       throw new IllegalStateException JavaDoc(iex.getMessage());
406     }
407     // Also clone the IApplicationData object.
408
// ---------------------------------------
409
try {
410       copy.setApplicationData(cloneObject(getApplicationData()));
411     }
412     catch (Exception JavaDoc ex) {
413       throw new IllegalStateException JavaDoc(ex.getMessage());
414     }
415     return copy;
416   }
417
418   /**
419    * Clones an object by using clone handlers. If no deep cloning possible, then
420    * return the reference.
421    *
422    * @param a_object the object to clone
423    * @return the cloned object, or the object itself if no coning supported
424    * @throws Exception
425    *
426    * @author Klaus Meffert
427    * @since 2.6
428    */

429   protected Object JavaDoc cloneObject(Object JavaDoc a_object)
430       throws Exception JavaDoc {
431     if (a_object == null) {
432       return null;
433     }
434     // Try to clone via a registered clone handler.
435
// --------------------------------------------
436
ICloneHandler cloner = getConfiguration().getJGAPFactory().
437         getCloneHandlerFor(a_object, a_object.getClass());
438     if (cloner != null) {
439       return cloner.perform(a_object, null, this);
440     }
441     else {
442       // No cloning supported, so just return the reference.
443
// ---------------------------------------------------
444
return a_object;
445     }
446   }
447
448   /**
449    * Returns the Gene at the given index (locus) within the Chromosome. The
450    * first gene is at index zero and the last gene is at the index equal to
451    * the size of this Chromosome - 1.
452    *
453    * @param a_desiredLocus index of the gene value to be returned
454    * @return Gene at the given index
455    *
456    * @author Neil Rotstan
457    * @since 1.0
458    */

459   public synchronized Gene getGene(int a_desiredLocus) {
460     return m_genes[a_desiredLocus];
461   }
462
463   /**
464    * Retrieves the set of genes that make up this Chromosome. This method
465    * exists primarily for the benefit of GeneticOperators that require the
466    * ability to manipulate Chromosomes at a low level.
467    *
468    * @return an array of the Genes contained within this Chromosome
469    *
470    * @author Neil Rotstan
471    * @since 1.0
472    */

473   public synchronized Gene[] getGenes() {
474     return m_genes;
475   }
476
477   /**
478    * Returns the size of this Chromosome (the number of genes it contains).
479    * A Chromosome's size is constant and will not change, until setGenes(...)
480    * is used.
481    *
482    * @return number of genes contained within this Chromosome instance
483    *
484    * @author Neil Rotstan
485    * @author Klaus Meffert
486    * @since 1.0
487    */

488   public int size() {
489     if (m_genes == null) {
490       // only possible when using default constructor
491
return 0;
492     }
493     else {
494       return m_genes.length;
495     }
496   }
497
498   /**
499    * Retrieves the fitness value of this Chromosome, as determined by the
500    * active fitness function. If a bulk fitness function is in use and
501    * has not yet assigned a fitness value to this Chromosome, then -1 is
502    * returned.<p>
503    * Attention: should not be called from toString() as the fitness value would
504    * be computed if it was initial!
505    *
506    * @return a positive double value representing the fitness of this
507    * Chromosome, or -1 if a bulk fitness function is in use and has not yet
508    * assigned a fitness value to this Chromosome
509    *
510    * @author Neil Rotstan
511    * @author Klaus Meffert
512    * @since 2.0 (until 1.1: return type int)
513    */

514   public double getFitnessValue() {
515     if (m_fitnessValue >= 0.000d) {
516       return m_fitnessValue;
517     }
518     else {
519       return calcFitnessValue();
520     }
521   }
522
523   /**
524    * @return the lastly computed fitness value, or FitnessFunction.NO_FITNESS_VALUE
525    * in case no value has been computed yet.
526    *
527    * @author Klaus Meffert
528    */

529   public double getFitnessValueDirectly() {
530     return m_fitnessValue;
531   }
532
533   /**
534    * @return fitness value of this chromosome determined via the registered
535    * fitness function
536    *
537    * @author Klaus Meffert
538    * @since 2.4
539    */

540   protected double calcFitnessValue() {
541     if (getConfiguration() != null) {
542       FitnessFunction normalFitnessFunction = getConfiguration().
543           getFitnessFunction();
544       if (normalFitnessFunction != null) {
545         // Grab the "normal" fitness function and ask it to calculate our
546
// fitness value.
547
// --------------------------------------------------------------
548
m_fitnessValue = normalFitnessFunction.getFitnessValue(this);
549       }
550     }
551     return m_fitnessValue;
552   }
553
554   /**
555    * Sets the fitness value of this Chromosome. This method is for use
556    * by bulk fitness functions and should not be invokved from anything
557    * else (except test cases).
558    *
559    * @param a_newFitnessValue a positive integer representing the fitness
560    * of this Chromosome
561    *
562    * @author Neil Rotstan
563    * @since 1.0
564    */

565   public void setFitnessValue(double a_newFitnessValue) {
566     if (a_newFitnessValue >= 0 &&
567         Math.abs(m_fitnessValue - a_newFitnessValue) > 0.0000001) {
568       m_fitnessValue = a_newFitnessValue;
569     }
570   }
571
572
573   /**
574    * Sets the fitness value of this Chromosome directly without any
575    * constraint checks, conversions or checks. Only use if you know what
576    * you do.
577    *
578    * @param a_newFitnessValue a positive integer representing the fitness
579    * of this Chromosome
580    *
581    * @author Klaus Meffert
582    */

583   public void setFitnessValueDirectly(double a_newFitnessValue) {
584     m_fitnessValue = a_newFitnessValue;
585   }
586
587   /**
588    * @return a string representation of this Chromosome, useful for display
589    * purposes
590    *
591    * @author Neil Rotstan
592    * @author Klaus Meffert
593    * @since 1.0
594    */

595   public String JavaDoc toString() {
596     StringBuffer JavaDoc representation = new StringBuffer JavaDoc();
597     representation.append(S_SIZE + ":" + size());
598     // Don't use getFitnessValue() here as it would then be initialized if
599
// it was not. We want to capture the current state here!
600
// -------------------------------------------------------------------
601
representation.append(", " + S_FITNESS_VALUE + ":" + m_fitnessValue);
602     representation.append(", " + S_ALLELES + ":");
603     representation.append("[");
604     // Append the representations of each of the genes' alleles.
605
// ---------------------------------------------------------
606
for (int i = 0; i < m_genes.length; i++) {
607       if (i > 0) {
608         representation.append(", ");
609       }
610       if (m_genes[i] == null) {
611         representation.append("null");
612       }
613       else {
614         representation.append(m_genes[i].toString());
615       }
616     }
617     representation.append("]");
618     String JavaDoc appData;
619     if (getApplicationData() != null) {
620       appData = getApplicationData().toString();
621     }
622     else {
623       appData = "null";
624     }
625     representation.append(", " + S_APPLICATION_DATA + ":" + appData);
626     return representation.toString();
627   }
628
629   /**
630    * Returns a persistent representation of this chromosome, see interface Gene
631    * for description. Similar to CompositeGene's routine. But does not include
632    * all information of the chromosome (yet).
633    *
634    * @return string representation of this Chromosome's relevant parts of its
635    * current state
636    * @throws UnsupportedOperationException
637    *
638    * @author Klaus Meffert
639    * @since 3.2
640    */

641   public String JavaDoc getPersistentRepresentation() {
642     StringBuffer JavaDoc b = new StringBuffer JavaDoc();
643     // Persist the chromosome's fitness value.
644
// ---------------------------------------
645
b.append(m_fitnessValue);
646     b.append(CHROM_DELIMITER);
647     // Persist the genes.
648
b.append(m_genes.length);
649     b.append(CHROM_DELIMITER);
650     Gene gene;
651     for (int i = 0; i < m_genes.length; i++) {
652       gene = m_genes[i];
653       b.append(GENE_DELIMITER_HEADING);
654       try {
655         b.append(URLEncoder.encode(gene.getClass().getName()
656                                    + GENE_DELIMITER
657                                    + gene.getPersistentRepresentation()
658                                    , "UTF-8"));
659       }
660       catch (UnsupportedEncodingException uex) {
661         throw new RuntimeException JavaDoc("UTF-8 should always be supported!", uex);
662       }
663       b.append(GENE_DELIMITER_CLOSING);
664     }
665     return b.toString();
666   }
667
668   /**
669    * Counterpart of getPersistentRepresentation.
670    *
671    * @param a_representation the string representation retrieved from a prior
672    * call to the getPersistentRepresentation() method
673    *
674    * @throws UnsupportedRepresentationException
675    *
676    * @author Klaus Meffert
677    * @since 3.2
678    */

679   public void setValueFromPersistentRepresentation(String JavaDoc a_representation)
680       throws UnsupportedRepresentationException {
681     if (a_representation != null) {
682       try {
683         List r = split(a_representation);
684         String JavaDoc g;
685         // Obtain fitness value.
686
// ---------------------
687
g = URLDecoder.decode( (String JavaDoc) r.get(0), "UTF-8");
688         setFitnessValue(Double.parseDouble(g));
689         r.remove(0);/**@todo we can do this faster!*/
690         // Obtain number of genes.
691
// -----------------------
692
g = URLDecoder.decode( (String JavaDoc) r.get(0), "UTF-8");
693         int count = Integer.parseInt(g);
694         m_genes = new Gene[count];
695         r.remove(0);/**@todo we can do this faster!*/
696         // Obtain the genes.
697
// -----------------
698
Iterator iter = r.iterator();
699         StringTokenizer st;
700         String JavaDoc clas;
701         String JavaDoc representation;
702         Gene gene;
703         int index = 0;
704         while (iter.hasNext()) {
705           g = URLDecoder.decode( (String JavaDoc) iter.next(), "UTF-8");
706           st = new StringTokenizer(g, GENE_DELIMITER);
707           if (st.countTokens() != 2)
708             throw new UnsupportedRepresentationException("In " + g + ", " +
709                 "expecting two tokens, separated by " + GENE_DELIMITER);
710           clas = st.nextToken();
711           representation = st.nextToken();
712           gene = createGene(clas, representation);
713           m_genes[index++] = gene;
714         }
715       }
716       catch (Exception JavaDoc ex) {
717         throw new UnsupportedRepresentationException(ex.toString());
718       }
719     }
720   }
721
722   /**
723    * Creates a new Gene instance.<p>
724    * Taken from CompositeGene.
725    *
726    * @param a_geneClassName name of the gene class
727    * @param a_persistentRepresentation persistent representation of the gene to
728    * create (could be obtained via getPersistentRepresentation)
729    *
730    * @return newly created gene
731    * @throws Exception
732    *
733    * @author Klaus Meffert
734    * @since 3.2
735    */

736   protected Gene createGene(String JavaDoc a_geneClassName,
737                             String JavaDoc a_persistentRepresentation)
738       throws Exception JavaDoc {
739     Class JavaDoc geneClass = Class.forName(a_geneClassName);
740     Constructor constr = geneClass.getConstructor(new Class JavaDoc[] {Configuration.class});
741     Gene gene = (Gene) constr.newInstance(new Object JavaDoc[] {getConfiguration()});
742     gene.setValueFromPersistentRepresentation(a_persistentRepresentation);
743     return gene;
744   }
745
746   /**
747    * Splits the input a_string into individual gene representations.<p>
748    * Taken and adapted from CompositeGene.
749    *
750    * @param a_string the string to split
751    * @return the elements of the returned array are the persistent
752    * representation strings of the chromosome's components
753    * @throws UnsupportedRepresentationException
754    *
755    * @author Klaus Meffert
756    * @since 3.2
757    */

758   protected static final List split(String JavaDoc a_string)
759       throws UnsupportedRepresentationException {
760     List a = Collections.synchronizedList(new ArrayList());
761     // Header data.
762
// ------------
763
int index = 0;
764     StringTokenizer st0 = new StringTokenizer
765         (a_string, CHROM_DELIMITER, false);
766     if (!st0.hasMoreTokens()) {
767       throw new UnsupportedRepresentationException("Fitness value expected!");
768     }
769     String JavaDoc fitnessS = st0.nextToken();
770     a.add(fitnessS);
771     index += fitnessS.length();
772     if (!st0.hasMoreTokens()) {
773       throw new UnsupportedRepresentationException("Number of genes expected!");
774     }
775     String JavaDoc numGenes = st0.nextToken();
776     a.add(numGenes);
777     index += numGenes.length();
778
779     index += 2; //2 one-character delimiters
780

781     if (!st0.hasMoreTokens()) {
782       throw new UnsupportedRepresentationException("Gene data missing!");
783     }
784
785     // Remove previously parsed content.
786
// ---------------------------------
787
a_string = a_string.substring(index);
788
789     // Gene data.
790
// ----------
791
StringTokenizer st = new StringTokenizer
792         (a_string, GENE_DELIMITER_HEADING + GENE_DELIMITER_CLOSING, true);
793     while (st.hasMoreTokens()) {
794       if (!st.nextToken().equals(GENE_DELIMITER_HEADING)) {
795         throw new UnsupportedRepresentationException(a_string + " no open tag");
796       }
797       String JavaDoc n = st.nextToken();
798       if (n.equals(GENE_DELIMITER_CLOSING)) {
799         a.add(""); /* Empty token */
800       }
801       else {
802         a.add(n);
803         if (!st.nextToken().equals(GENE_DELIMITER_CLOSING)) {
804           throw new UnsupportedRepresentationException
805               (a_string + " no close tag");
806         }
807       }
808     }
809     return a;
810   }
811
812   /**
813    * Convenience method that returns a new Chromosome instance with its
814    * genes values (alleles) randomized. Note that, if possible, this method
815    * will acquire a Chromosome instance from the active ChromosomePool
816    * (if any) and then randomize its gene values before returning it. If a
817    * Chromosome cannot be acquired from the pool, then a new instance will
818    * be constructed and its gene values randomized before returning it.
819    *
820    * @param a_configuration the configuration to use
821    * @return randomly initialized Chromosome
822    * @throws InvalidConfigurationException if the given Configuration
823    * instance is invalid
824    * @throws IllegalArgumentException if the given Configuration instance
825    * is null
826    *
827    * @author Neil Rotstan
828    * @author Klaus Meffert
829    * @since 1.0
830    */

831   public static IChromosome randomInitialChromosome(
832       Configuration a_configuration)
833       throws InvalidConfigurationException {
834     // Sanity check: make sure the given configuration isn't null.
835
// -----------------------------------------------------------
836
if (a_configuration == null) {
837       throw new IllegalArgumentException JavaDoc(
838           "Configuration instance must not be null");
839     }
840     // Lock the configuration settings so that they can't be changed
841
// from now on.
842
// -------------------------------------------------------------
843
a_configuration.lockSettings();
844     // First see if we can get a Chromosome instance from the pool.
845
// If we can, we'll randomize its gene values (alleles) and then
846
// return it.
847
// -------------------------------------------------------------
848
IChromosomePool pool = a_configuration.getChromosomePool();
849     if (pool != null) {
850       IChromosome randomChromosome = pool.acquireChromosome();
851       if (randomChromosome != null) {
852         Gene[] genes = randomChromosome.getGenes();
853         RandomGenerator generator = a_configuration.getRandomGenerator();
854         for (int i = 0; i < genes.length; i++) {
855           genes[i].setToRandomValue(generator);
856           /**@todo what about Gene's energy?*/
857         }
858         randomChromosome.setFitnessValueDirectly(FitnessFunction.
859                                                  NO_FITNESS_VALUE);
860         return randomChromosome;
861       }
862     }
863     // We weren't able to get a Chromosome from the pool, so we have to
864
// construct a new instance and build it from scratch.
865
// ------------------------------------------------------------------
866
IChromosome sampleChromosome =
867         a_configuration.getSampleChromosome();
868     sampleChromosome.setFitnessValue(FitnessFunction.NO_FITNESS_VALUE);
869     Gene[] sampleGenes = sampleChromosome.getGenes();
870     Gene[] newGenes = new Gene[sampleGenes.length];
871     RandomGenerator generator = a_configuration.getRandomGenerator();
872     for (int i = 0; i < newGenes.length; i++) {
873       // We use the newGene() method on each of the genes in the
874
// sample Chromosome to generate our new Gene instances for
875
// the Chromosome we're returning. This guarantees that the
876
// new Genes are setup with all of the correct internal state
877
// for the respective gene position they're going to inhabit.
878
// -----------------------------------------------------------
879
newGenes[i] = sampleGenes[i].newGene();
880       // Set the gene's value (allele) to a random value.
881
// ------------------------------------------------
882
newGenes[i].setToRandomValue(generator);
883       /**@todo what about Gene's energy?*/
884     }
885     // Finally, construct the new chromosome with the new random
886
// genes values and return it.
887
// ---------------------------------------------------------
888
return new Chromosome(a_configuration, newGenes);
889   }
890
891   /**
892    * Compares this Chromosome against the specified object. The result is
893    * true if and the argument is an instance of the Chromosome class
894    * and has a set of genes equal to this one.
895    *
896    * @param other the object to compare against
897    * @return true: if the objects are the same, false otherwise
898    *
899    * @author Neil Rotstan
900    * @author Klaus Meffert
901    * @since 1.0
902    */

903   public boolean equals(Object JavaDoc other) {
904     // If class is not equal, return false. Therefor catch
905
// ClasscastException's. The cleaner way (commented out below) would
906
// be too slow, indeed.
907
// -----------------------------------------------------------------
908
/*
909        if (other != null &&
910         !this.getClass ().getName ().equals (other.getClass ().getName ()))
911         {
912             return false;
913         }
914      */

915     try {
916       return compareTo(other) == 0;
917     }
918     catch (ClassCastException JavaDoc cex) {
919       return false;
920     }
921   }
922
923   /**
924    * Retrieve a hash code for this Chromosome. Does not considers the order
925    * of the Genes for all cases (especially when gene is empty).
926    *
927    * @return the hash code of this Chromosome
928    *
929    * @author Neil Rotstan
930    * @author Klaus Meffert
931    * @since 1.0
932    */

933   public int hashCode() {
934     // Do what {@link java.util.AbstractList} does.
935
// --------------------------------------------
936
int geneHashcode;
937     int hashCode = 1;
938     if (m_genes != null) {
939       for (int i = 0; i < m_genes.length; i++) {
940         geneHashcode = m_genes[i].hashCode();
941         hashCode = 31 * hashCode + geneHashcode;
942       }
943     }
944     return hashCode;
945   }
946
947   /**
948    * Compares the given Chromosome to this Chromosome. This chromosome is
949    * considered to be "less than" the given chromosome if it has a fewer
950    * number of genes or if any of its gene values (alleles) are less than
951    * their corresponding gene values in the other chromosome.
952    *
953    * @param other the Chromosome against which to compare this chromosome
954    * @return a negative number if this chromosome is "less than" the given
955    * chromosome, zero if they are equal to each other, and a positive number if
956    * this chromosome is "greater than" the given chromosome
957    *
958    * @author Neil Rotstan
959    * @author Klaus Meffert
960    * @since 1.0
961    */

962   public int compareTo(Object JavaDoc other) {
963     // First, if the other Chromosome is null, then this chromosome is
964
// automatically the "greater" Chromosome.
965
// ---------------------------------------------------------------
966
if (other == null) {
967       return 1;
968     }
969     int size = size();
970     IChromosome otherChromosome = (IChromosome) other;
971     Gene[] otherGenes = otherChromosome.getGenes();
972     // If the other Chromosome doesn't have the same number of genes,
973
// then whichever has more is the "greater" Chromosome.
974
// --------------------------------------------------------------
975
if (otherChromosome.size() != size) {
976       return size() - otherChromosome.size();
977     }
978     // Next, compare the gene values (alleles) for differences. If
979
// one of the genes is not equal, then we return the result of its
980
// comparison.
981
// ---------------------------------------------------------------
982
for (int i = 0; i < size; i++) {
983       int comparison = m_genes[i].compareTo(otherGenes[i]);
984       if (comparison != 0) {
985         return comparison;
986       }
987     }
988     // Compare current fitness value.
989
// ------------------------------
990
if (m_fitnessValue != otherChromosome.getFitnessValueDirectly()) {
991       FitnessEvaluator eval = getConfiguration().getFitnessEvaluator();
992       if (eval != null) {
993         if (eval.isFitter(m_fitnessValue,
994                           otherChromosome.getFitnessValueDirectly())) {
995           return 1;
996         }
997         else {
998           return -1;
999         }
1000      }
1001      else {
1002        // undetermined order, but unequal!
1003
// --------------------------------
1004
return -1;
1005      }
1006    }
1007    if (m_compareAppData) {
1008      // Compare application data.
1009
// -------------------------
1010
if (getApplicationData() == null) {
1011        if (otherChromosome.getApplicationData() != null) {
1012          return -1;
1013        }
1014      }
1015      else if (otherChromosome.getApplicationData() == null) {
1016        return 1;
1017      }
1018      else {
1019        if (getApplicationData() instanceof Comparable JavaDoc) {
1020          try {
1021            return ( (Comparable JavaDoc) getApplicationData()).compareTo(
1022                otherChromosome.getApplicationData());
1023          }
1024          catch (ClassCastException JavaDoc cex) {
1025            /**@todo improve*/
1026            return -1;
1027          }
1028        }
1029        else {
1030          return getApplicationData().getClass().getName().compareTo(
1031              otherChromosome.getApplicationData().getClass().getName());
1032        }
1033      }
1034    }
1035    // Everything is equal. Return zero.
1036
// ---------------------------------
1037
return 0;
1038  }
1039
1040  /**
1041   * Sets whether this Chromosome has been selected by the natural selector
1042   * to continue to the next generation or manually (e.g. via an add-method).
1043   *
1044   * @param a_isSelected true if this Chromosome has been selected, false
1045   * otherwise
1046   *
1047   * @author Neil Rotstan
1048   * @since 1.0
1049   */

1050  public void setIsSelectedForNextGeneration(boolean a_isSelected) {
1051    m_isSelectedForNextGeneration = a_isSelected;
1052  }
1053
1054  /**
1055   * Retrieves whether this Chromosome has been selected by the natural
1056   * selector to continue to the next generation.
1057   *
1058   * @return true if this Chromosome has been selected, false otherwise
1059   *
1060   * @author Neil Rotstan
1061   * @since 1.0
1062   */

1063  public boolean isSelectedForNextGeneration() {
1064    return m_isSelectedForNextGeneration;
1065  }
1066
1067  /**
1068   * Invoked when this Chromosome is no longer needed and should perform
1069   * any necessary cleanup. Note that this method will attempt to release
1070   * this Chromosome instance to the active ChromosomePool, if any.
1071   *
1072   * @author Neil Rotstan
1073   * @since 1.0
1074   */

1075  public void cleanup() {
1076    if (getConfiguration() == null) {
1077      throw new IllegalStateException JavaDoc(
1078          "The active Configuration object must be set on this " +
1079          "Chromosome prior to invocation of the cleanup() method.");
1080    }
1081    // First, reset our internal state.
1082
// --------------------------------
1083
m_fitnessValue = getConfiguration().getFitnessFunction().
1084        NO_FITNESS_VALUE;
1085    m_isSelectedForNextGeneration = false;
1086    // Next we want to try to release this Chromosome to a ChromosomePool
1087
// if one has been setup so that we can save a little time and memory
1088
// next time a Chromosome is needed.
1089
// ------------------------------------------------------------------
1090
// Now fetch the active ChromosomePool from the Configuration object
1091
// and, if the pool exists, release this Chromosome to it.
1092
// -----------------------------------------------------------------
1093
IChromosomePool pool = getConfiguration().getChromosomePool();
1094    if (pool != null) {
1095      // Note that the pool will take care of any gene cleanup for us,
1096
// so we don't need to worry about it here.
1097
// -------------------------------------------------------------
1098
pool.releaseChromosome(this);
1099    }
1100    else {
1101      // No pool is available, so we need to finish cleaning up, which
1102
// basically entails requesting each of our genes to clean
1103
// themselves up as well.
1104
// -------------------------------------------------------------
1105
for (int i = 0; i < size(); i++) {
1106        m_genes[i].cleanup();
1107      }
1108    }
1109  }
1110
1111  /**
1112   * This sets the application-specific data that is attached to this
1113   * Chromosome. Attaching application-specific data may be useful for
1114   * some applications when it comes time to evaluate this Chromosome
1115   * in the fitness function. JGAP ignores this data, except for cloning and
1116   * comparison (latter only if opted in via setCompareApplicationData(..))
1117   *
1118   * @param a_newData the new application-specific data to attach to this
1119   * Chromosome. Should be an instance of IApplicationData
1120   *
1121   * @author Neil Rotstan
1122   * @since 1.1
1123   */

1124  public void setApplicationData(Object JavaDoc a_newData) {
1125    m_applicationData = a_newData;
1126  }
1127
1128  /**
1129   * Retrieves the application-specific data that is attached to this
1130   * Chromosome. Attaching application-specific data may be useful for
1131   * some applications when it comes time to evaluate this Chromosome
1132   * in the fitness function. JGAP ignores this data functionally.
1133   *
1134   * @return the application-specific data previously attached to this
1135   * Chromosome, or null if there is no data attached
1136   *
1137   * @author Neil Rotstan
1138   * @since 1.1
1139   */

1140  public Object JavaDoc getApplicationData() {
1141    return m_applicationData;
1142  }
1143
1144  /**
1145   * Sets the genes for the chromosome.
1146   * @param a_genes the genes to set for the chromosome
1147   *
1148   * @throws InvalidConfigurationException in case constraint checker is
1149   * provided
1150   *
1151   * @author Klaus Meffert
1152   */

1153  public void setGenes(Gene[] a_genes)
1154      throws InvalidConfigurationException {
1155// for (int i=0;i<a_genes.length;i++) {
1156
// if (a_genes[i]==null) {
1157
// throw new RuntimeException("Gene may not be null!");
1158
// }
1159
// }
1160
m_genes = a_genes;
1161    verify(getConstraintChecker());
1162  }
1163
1164  /**
1165   * Should we also consider the application data when comparing? Default is
1166   * "false" as "true" means a Chromosome is losing its identity when
1167   * application data is set differently!
1168   *
1169   * @param a_doCompare true: consider application data in method compareTo
1170   *
1171   * @author Klaus Meffert
1172   * @since 2.2
1173   */

1174  public void setCompareApplicationData(boolean a_doCompare) {
1175    m_compareAppData = a_doCompare;
1176  }
1177
1178  /*
1179   * @return should we also consider the application data when comparing?
1180   *
1181   * @author Klaus Meffert
1182   * @since 2.2
1183   */

1184  public boolean isCompareApplicationData() {
1185    return m_compareAppData;
1186  }
1187
1188  /**
1189   * Sets the constraint checker to be used for this gene whenever method
1190   * setAllele(Object) is called.
1191   *
1192   * @param a_constraintChecker the constraint checker to be set
1193   * @throws InvalidConfigurationException
1194   *
1195   * @author Klaus Meffert
1196   * @since 2.5
1197   */

1198  public void setConstraintChecker(IGeneConstraintChecker a_constraintChecker)
1199      throws InvalidConfigurationException {
1200    verify(a_constraintChecker);
1201    m_geneAlleleChecker = a_constraintChecker;
1202  }
1203
1204  /**
1205   * @return IGeneConstraintChecker the constraint checker to be used whenever
1206   * method setGenes(Gene[]) is called.
1207   *
1208   * @author Klaus Meffert
1209   * @since 2.5
1210   */

1211  public IGeneConstraintChecker getConstraintChecker() {
1212    return m_geneAlleleChecker;
1213  }
1214
1215  /**
1216   * Verifies the state of the chromosome. Especially takes care of the
1217   * given constraint checker.
1218   * @param a_constraintChecker the constraint checker to verify
1219   *
1220   * @throws InvalidConfigurationException
1221   *
1222   * @author Klaus Meffert
1223   * @since 2.5
1224   */

1225  protected void verify(IGeneConstraintChecker a_constraintChecker)
1226      throws InvalidConfigurationException {
1227    if (a_constraintChecker != null && getGenes() != null) {
1228      int len = getGenes().length;
1229      for (int i = 0; i < len; i++) {
1230        Gene gene = getGene(i);
1231        if (!a_constraintChecker.verify(gene, null, this, i)) {
1232          throw new InvalidConfigurationException(
1233              "The gene type "
1234              + gene.getClass().getName()
1235              + " is not allowed to be used in the chromosome due to the"
1236              + " constraint checker used.");
1237        }
1238      }
1239    }
1240  }
1241
1242  // ------------------------------------
1243
// Begin of IInitializer implementation
1244
// ------------------------------------
1245

1246  /**{@inheritDoc}*/
1247  public boolean isHandlerFor(Object JavaDoc a_obj, Class JavaDoc a_class) {
1248    if (a_class == Chromosome.class) {
1249      return true;
1250    }
1251    else {
1252      return false;
1253    }
1254  }
1255
1256  /**{@inheritDoc}*/
1257  public Object JavaDoc perform(Object JavaDoc a_obj, Class JavaDoc a_class, Object JavaDoc a_params)
1258      throws Exception JavaDoc {
1259    return randomInitialChromosome(getConfiguration());
1260  }
1261
1262  // ----------------------------------
1263
// End of IInitializer implementation
1264
// ----------------------------------
1265
public void setMultiObjectives(List a_values) {
1266    if (m_multiObjective == null) {
1267      m_multiObjective = new Vector();
1268    }
1269    m_multiObjective.clear();
1270    m_multiObjective.addAll(a_values);
1271  }
1272
1273  public List getMultiObjectives() {
1274    return m_multiObjective;
1275  }
1276}
1277
Popular Tags