KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jgap > supergenes > AbstractSupergene


1 /*
2  * This file is part of JGAP.
3  *
4  * JGAP offers a dual license model containing the LGPL as well as the MPL.
5  *
6  * For licencing information please see the file license.txt included with JGAP
7  * or have a look at the top of class org.jgap.Chromosome which representatively
8  * includes the JGAP license policy applicable for any file delivered with JGAP.
9  */

10 package org.jgap.supergenes;
11
12 import java.io.*;
13 import java.lang.reflect.*;
14 import java.net.*;
15 import java.util.*;
16 import org.jgap.*;
17
18 /**
19  * Combined implementation of both Supergene and SupergeneValidator.
20  * A working supergene can be easily created from this class just by
21  * adding genes and overriding
22  * {@link org.jgap.supergenes.AbstractSupergene#isValid(Gene [] a_case,
23  * Supergene a_forSupergene) isValid (Gene [], Supergene)}
24  * method. For more complex cases, you may need to set your own
25  * {@link org.jgap.supergenes.Validator Validator}.
26  *
27  * @author Audrius Meskauskas
28  * @since 2.0
29  */

30 public abstract class AbstractSupergene
31     extends BaseGene
32     implements Supergene, SupergeneValidator, IPersistentRepresentation {
33   /** String containing the CVS revision. Read out via reflection!*/
34   private final static String JavaDoc CVS_REVISION = "$Revision: 1.21 $";
35
36   /**
37    * This field separates gene class name from
38    * the gene persistent representation string.
39    */

40   public final static String JavaDoc GENE_DELIMITER = "#";
41
42   /**
43    * Represents the heading delimiter that is used to separate genes in the
44    * persistent representation of CompositeGene instances.
45    */

46   public final static String JavaDoc GENE_DELIMITER_HEADING = "<";
47
48   /**
49    * Represents the closing delimiter that is used to separate genes in the
50    * persistent representation of CompositeGene instances.
51    */

52   public final static String JavaDoc GENE_DELIMITER_CLOSING = ">";
53
54   /**
55    * Maximal number of retries for applyMutation and setToRandomValue.
56    * If the valid supergen cannot be created after this number of iterations,
57    * the error message is printed and the unchanged instance is returned.
58    * */

59   public final static int MAX_RETRIES = 1;
60
61   /**
62    * Maximal number of notes about immutable genes per
63    * single gene position
64    * */

65   public final static int MAX_IMMUTABLE_GENES = 100000;
66
67   /** Holds the genes of this supergene. */
68   private Gene[] m_genes;
69
70   /** Set of supergene allele values that cannot mutate. */
71   private static Set[] m_immutable = new Set[1];
72
73   /**
74    * @return the array of genes - components of this supergene. The supergene
75    * components may be supergenes itself
76    */

77   public Gene[] getGenes() {
78     return m_genes;
79   }
80
81   /**
82    * Returns the Gene at the given index (locus) within the Chromosome. The
83    * first gene is at index zero and the last gene is at the index equal to
84    * the size of this Chromosome - 1.
85    *
86    * This seems to be one of the bottlenecks, so it is declared final.
87    * I cannot imagine the reason for overriding this trivial single line
88    * method.
89    *
90    * @param a_index the index of the gene value to be returned
91    * @return the Gene at the given index
92    */

93   public final Gene geneAt(final int a_index) {
94     return m_genes[a_index];
95   };
96
97   /**
98    * Default constructor for dynamic instantiation.
99    *
100    * @throws InvalidConfigurationException
101    *
102    * @author Klaus Meffert
103    * @since 3.0
104    */

105   public AbstractSupergene()
106       throws InvalidConfigurationException {
107     this(Genotype.getStaticConfiguration(), new Gene[]{});
108   }
109
110   /**
111    * Constructor for dynamic instantiation.
112    *
113    * @param a_config the configuration to use
114    * @throws InvalidConfigurationException
115    *
116    * @author Klaus Meffert
117    * @since 3.0
118    */

119   public AbstractSupergene(final Configuration a_config)
120       throws InvalidConfigurationException {
121     this(a_config, new Gene[]{});
122   }
123
124   /**
125    * Constructs abstract supergene with the given gene list.
126    *
127    * @param a_conf the configuration to use
128    * @param a_genes array of genes for this Supergene
129    * @throws InvalidConfigurationException
130    */

131   public AbstractSupergene(final Configuration a_conf, final Gene[] a_genes)
132       throws InvalidConfigurationException {
133     super(a_conf);
134     if (a_genes == null) {
135       throw new RuntimeException JavaDoc("null value for genes not allowed!");
136     }
137     m_genes = a_genes;
138   }
139
140   /**
141    * Test the allele combination of this supergene for validity. This method
142    * calls isValid for the current gene list.
143    * @return true only if the supergene allele combination is valid
144    * or the setValidator (<i>null</i>) has been previously called
145    */

146   public boolean isValid() {
147     if (m_validator == null) {
148       return true;
149     }
150     else {
151       return m_validator.isValid(m_genes, this);
152     }
153   }
154
155   /**
156    * Test the given gene list for validity. The genes must exactly the same
157    * as inside this supergene.
158    * At <i>least about 5 % of the randomly
159    * generated Supergene suparallele values should be valid.</i> If the valid
160    * combinations represents too small part of all possible combinations,
161    * it can take too long to find the suitable mutation that does not brake
162    * a supergene. If you face this problem, try to split the supergene into
163    * several sub-supergenes.
164    *
165    * This method is only called if you have not set any alternative
166    * validator (including <i>null</i>).
167    *
168    * @param a_case ignored here
169    * @param a_forSupergene ignored here
170    *
171    * @return true only if the supergene allele combination is valid
172    * @throws Error by default. If you do not set external validator,
173    * you should always override this method
174    */

175   public boolean isValid(final Gene[] a_case, final Supergene a_forSupergene) {
176     throw new Error JavaDoc("For " + getClass().getName() + ", override "
177                     + " isValid (Gene[], Supergene) or set an"
178                     + " external validator.");
179   }
180
181   /**
182    * Creates a new instance of this Supergene class with the same number of
183    * genes, calling newGene() for each subgene. The class, derived from this
184    * abstract supergene will be instantiated
185    * (not the instance of abstractSupergene itself). If the external
186    * validator is set, the same validator will be set for the new gene.
187    *
188    * @return the new Gene
189    * @throws Error if the instance of <i>this</i> cannot be instantiated
190    * (for example, if it is not public or the parameterless constructor is
191    * not provided).
192    * */

193   protected Gene newGeneInternal() {
194     Gene[] g = new Gene[m_genes.length];
195     for (int i = 0; i < m_genes.length; i++) {
196       g[i] = m_genes[i].newGene();
197     }
198     try {
199       Constructor constr = getClass().getConstructor(new Class JavaDoc[] {Configuration.class, Gene[].class});
200       AbstractSupergene age =
201           (AbstractSupergene) constr.newInstance(new Object JavaDoc[] {getConfiguration(), getGenes()});
202       if (m_validator != this) {
203         age.setValidator(m_validator);
204       }
205       age.m_genes = g;
206       return age;
207     }
208     catch (Exception JavaDoc ex) {
209       ex.printStackTrace();
210       throw new Error JavaDoc(
211           "This should not happen. Is the constructor with parameters "
212           + "{org.jgap.Configuration, org,jgap,Gene[]} provided for "
213           + getClass().getName() + "?");
214     }
215   }
216
217   /**
218    * Applies a mutation of a given intensity (percentage) onto the gene
219    * at the given index. Retries while isValid() returns true for the
220    * supergene. The method is delegated to the first element ] of the
221    * gene, indexed by <code>index</code>.
222    * See org.jgap.supergenes.AbstractSupergene.isValid()
223    */

224   public void applyMutation(final int a_index, final double a_percentage) {
225     // Return immediately the current value is found in
226
// the list of immutable alleles for this position.
227
// ---------------------------------------------------
228
if (a_index < m_immutable.length) {
229       if (m_immutable[a_index] != null) {
230         synchronized (m_immutable) {
231           if (m_immutable[a_index].contains(this)) {
232             return;
233           }
234         }
235       }
236     }
237     // Following commented out because if only very few valid states exist it
238
// may be that they are not reached within a given number of tries.
239
// ----------------------------------------------------------------------
240
// if (!isValid()) {
241
// throw new Error("Should be valid on entry");
242
// }
243
Object JavaDoc backup = m_genes[a_index].getAllele();
244     for (int i = 0; i < MAX_RETRIES; i++) {
245       m_genes[a_index].applyMutation(0, a_percentage);
246       if (isValid()) {
247         return;
248       }
249     }
250     // restore the gene as it was
251
m_genes[a_index].setAllele(backup);
252     markImmutable(a_index);
253   }
254
255   /** @todo: Implement protection against overgrowing of this
256    * data block.
257    */

258   private void markImmutable(final int a_index) {
259     synchronized (m_immutable) {
260       if (m_immutable.length <= a_index) {
261         // Extend the array (double length).
262
// ---------------------------------
263
Set[] r = new Set[2 * m_immutable.length];
264         System.arraycopy(m_immutable, 0, r, 0, m_immutable.length);
265         m_immutable = r;
266       }
267       if (m_immutable[a_index] == null) {
268         m_immutable[a_index] = new TreeSet();
269       }
270       if (m_immutable[a_index].size() < MAX_IMMUTABLE_GENES) {
271         m_immutable[a_index].add(this);
272       }
273     }
274     ;
275   }
276
277   /**
278    * Discards all internal caches, ensuring correct repetetive tests
279    * of performance. Differently from cleanup(), discards also static
280    * references, that are assumed to be useful for the multiple instances
281    * of the Supergene.
282    * Clears the set of the alleles that are known to be immutable.
283    */

284   public static void reset() {
285     m_immutable = new Set[1];
286   }
287
288   /**
289    * Sets the value of this Gene to a random legal value for the
290    * implementation. It calls setToRandomValue for all subgenes and
291    * then validates. With a large number of subgenes and low percent of
292    * valid combinations this may take too long to complete. We think,
293    * at lease several % of the all possible combintations must be valid.
294    */

295   public void setToRandomValue(final RandomGenerator a_numberGenerator) {
296     // set all to random value first
297
for (int i = 0; i < m_genes.length; i++) {
298       m_genes[i].setToRandomValue(a_numberGenerator);
299     }
300     if (isValid()) {
301       return;
302     }
303     for (int i = 0; i < MAX_RETRIES; i++) {
304       for (int j = 0; j < m_genes.length; j++) {
305         // Mutate only one gene at time.
306
// -----------------------------
307
m_genes[j].setToRandomValue(a_numberGenerator);
308         if (isValid()) {
309           return;
310         }
311       }
312     }
313   }
314
315   /**
316    * Sets the allele.
317    * @param a_superAllele must be an array of objects, size matching the
318    * number of genes
319    */

320   public void setAllele(final Object JavaDoc a_superAllele) {
321     if (m_genes.length < 1) {
322       // Nothing to do
323
return;
324     }
325     Object JavaDoc[] a = (Object JavaDoc[]) a_superAllele;
326     if (a.length != m_genes.length) {
327       throw new IllegalArgumentException JavaDoc("Record length, " + a.length
328                                    + " not equal to "
329                                    + m_genes.length);
330     }
331     for (int i = 0; i < m_genes.length; i++) {
332       m_genes[i].setAllele(a[i]);
333     }
334   }
335
336   /**
337    * Retrieves the allele value represented by this Supergene.
338    * @return array of objects, each matching the subgene in this Supergene
339    */

340   public Object JavaDoc getAllele() {
341     Object JavaDoc[] o = new Object JavaDoc[m_genes.length];
342     for (int i = 0; i < m_genes.length; i++) {
343       o[i] = m_genes[i].getAllele();
344     }
345     return o;
346   }
347
348   /**
349    * @return a string representation of the value of this Supergene
350    * instance, using calls to the Supergene components. Supports other
351    * (nested) supergenes in this supergene
352    */

353   public String JavaDoc getPersistentRepresentation() {
354     StringBuffer JavaDoc b = new StringBuffer JavaDoc();
355     // Write validator:
356
String JavaDoc validator = null;
357     String JavaDoc v_representation = "";
358     SupergeneValidator v = getValidator();
359     if (v == null) {
360       validator = "null";
361     }
362     else
363     if (v == this) {
364       validator = "this";
365     }
366     else {
367       validator = v.getClass().getName();
368       v_representation = v.getPersistent();
369     }
370     b.append(GENE_DELIMITER_HEADING);
371     b.append(encode(validator + GENE_DELIMITER + v_representation));
372     b.append(GENE_DELIMITER_CLOSING);
373     // Write genes:
374
Gene gene;
375     for (int i = 0; i < m_genes.length; i++) {
376       gene = m_genes[i];
377       b.append(GENE_DELIMITER_HEADING);
378       b.append(encode(gene.getClass().getName() + GENE_DELIMITER
379                       + gene.getPersistentRepresentation()));
380       b.append(GENE_DELIMITER_CLOSING);
381     }
382     return b.toString();
383   }
384
385   /**
386    * Sets the value and internal state of this Gene from the string
387    * representation returned by a previous invocation of the
388    * getPersistentRepresentation() method.
389    *
390    * If the validator is not THIS and not null, a new validator is
391    * created using Class.forName(..).newInstance.
392    *
393    * @param a_representation the string representation retrieved from a
394    * prior call to the getPersistentRepresentation() method
395    *
396    * @throws UnsupportedRepresentationException
397    *
398    * @author Audrius Meskauskas
399    * @since 2.0
400    */

401   public void setValueFromPersistentRepresentation(String JavaDoc a_representation)
402       throws UnsupportedRepresentationException {
403     if (a_representation != null) {
404       try {
405         /// Remove the old content.
406
// ------------------------
407
List r = split(a_representation);
408         Iterator iter = r.iterator();
409         m_genes = new Gene[r.size() - 1];
410         // The first member in array is a validator representation.
411
// --------------------------------------------------------
412
StringTokenizer st;
413         String JavaDoc clas;
414         String JavaDoc representation;
415         String JavaDoc g;
416         Gene gene;
417         String JavaDoc validator = (String JavaDoc) iter.next();
418         setValidator(createValidator(decode(validator)));
419         for (int i = 0; i < m_genes.length; i++) {
420           g = decode( (String JavaDoc) iter.next());
421           st = new StringTokenizer(g, GENE_DELIMITER);
422           if (st.countTokens() != 2)
423             throw new UnsupportedRepresentationException("In " + g + ", " +
424                 "expecting two tokens, separated by " + GENE_DELIMITER);
425           clas = st.nextToken();
426           representation = st.nextToken();
427           gene = createGene(clas, representation);
428           m_genes[i] = gene;
429         }
430       }
431       catch (Exception JavaDoc ex) {
432         ex.printStackTrace();
433         throw new UnsupportedRepresentationException(ex.getCause().
434             getMessage());
435       }
436     }
437     else {
438       throw new UnsupportedRepresentationException("null value not allowed");
439     }
440   }
441
442   /** Create validator from the string representation. */
443   protected SupergeneValidator createValidator(String JavaDoc a_rep) {
444     try {
445       StringTokenizer vo = new StringTokenizer
446           (a_rep, GENE_DELIMITER, true);
447       if (vo.countTokens() != 2)throw new Error JavaDoc
448           ("In " + a_rep + ", expecting two tokens, separated by " +
449            GENE_DELIMITER);
450       String JavaDoc clas = vo.nextToken();
451       SupergeneValidator sv;
452       if (clas.equals("this")) {
453         sv = this;
454       }
455       else if (clas.equals("null")) {
456         sv = null;
457       }
458       else {
459 // sv = (SupergeneValidator) Class.forName(clas).newInstance();
460
Class JavaDoc svClass = Class.forName(clas);
461         Constructor constr = svClass.getConstructor(new Class JavaDoc[] {Configuration.class});
462         sv = (SupergeneValidator) constr.newInstance(new Object JavaDoc[] {
463             getConfiguration()});
464       }
465       if (sv != null) {
466         sv.setFromPersistent(decode(vo.nextToken()));
467       }
468       return sv;
469     }
470     catch (Exception JavaDoc ex) {
471       throw new Error JavaDoc
472           ("Unable to create validator from '" + a_rep + "' for " +
473            getClass().getName(), ex);
474     }
475   }
476
477   /** Creates a new instance of gene. */
478   protected Gene createGene(String JavaDoc a_geneClassName,
479                             String JavaDoc a_persistentRepresentation)
480       throws Exception JavaDoc {
481     Class JavaDoc geneClass = Class.forName(a_geneClassName);
482     Constructor constr = geneClass.getConstructor(new Class JavaDoc[] {Configuration.class});
483     Gene gene = (Gene) constr.newInstance(new Object JavaDoc[] {getConfiguration()});
484     gene.setValueFromPersistentRepresentation(a_persistentRepresentation);
485     return gene;
486   }
487
488   /** Calls cleanup() for each subgene. */
489   public void cleanup() {
490     for (int i = 0; i < m_genes.length; i++) {
491       m_genes[i].cleanup();
492     }
493   }
494
495   /**
496    * @return a string representation of the supergene, providing
497    * class name and calling toString() for all subgenes.
498    */

499   public String JavaDoc toString() {
500     StringBuffer JavaDoc b = new StringBuffer JavaDoc();
501     b.append("Supergene " + getClass().getName() + " {");
502     for (int i = 0; i < m_genes.length; i++) {
503       b.append("|");
504       b.append(m_genes[i].toString());
505       b.append("|");
506     }
507     if (m_validator == null) {
508       b.append(" non validating");
509     }
510     else {
511       b.append(" validator: "+m_validator.getClass().getName());
512     }
513     b.append("}");
514     return b.toString();
515   }
516
517   /** Returns the number of the genes-components of this supergene. */
518   public int size() {
519     return m_genes.length;
520   }
521
522   /** Calls compareTo() for all subgenes. The passed parameter must be
523    * an instance of AbstractSupergene. */

524   public int compareTo(Object JavaDoc o) {
525     AbstractSupergene q = (AbstractSupergene) o;
526     int c = m_genes.length - q.m_genes.length;
527     if (c != 0) {
528       return c;
529     }
530     for (int i = 0; i < m_genes.length; i++) {
531       c = m_genes[i].compareTo(q.m_genes[i]);
532       if (c != 0) {
533         return c;
534       }
535     }
536     if (getClass().equals(o.getClass())) {
537       return 0;
538     }
539     return getClass().getName().compareTo(o.getClass().getName());
540   }
541
542   /**
543    * Calls equals() for each pair of genes. If the supplied object is
544    * an instance of the different class, returns false. Also, the
545    * genes are assumed to be different if they have different validator
546    * classes (or only one of the validators is set to null).
547    */

548   public boolean equals(Object JavaDoc a_gene) {
549     if (a_gene == null || ! (a_gene.getClass().equals(getClass()))) {
550       return false;
551     }
552     AbstractSupergene age = (AbstractSupergene) a_gene;
553     if (m_validator != age.m_validator)
554       if (m_validator != null && age.m_immutable != null)
555         if (!m_validator.getClass().equals(age.m_validator.getClass()))
556           return false;
557     return Arrays.equals(m_genes, age.m_genes);
558   }
559
560   /** Returns sum of hashCode() of the genes-components. */
561   public int hashCode() {
562     int s = 0;
563     for (int i = m_genes.length - 1; i >= 0; i--) {
564       s += m_genes[i].hashCode();
565     }
566     return s;
567   }
568
569   /* Encode string, doubling the separators. */
570   protected static final String JavaDoc encode(String JavaDoc a_x) {
571     try {
572       return URLEncoder.encode(a_x, "UTF-8");
573     }
574     catch (UnsupportedEncodingException ex) {
575       throw new Error JavaDoc("This should never happen!");
576     }
577   }
578
579   /** Decode string, undoubling the separators. */
580   protected static final String JavaDoc decode(String JavaDoc a_x) {
581     try {
582       return URLDecoder.decode(a_x, "UTF-8");
583     }
584     catch (UnsupportedEncodingException ex) {
585       throw new Error JavaDoc("This should never happen!");
586     }
587   }
588
589   /**
590    * Splits the string a_x into individual gene representations
591    * @param a_string the string to split
592    * @return the elements of the returned array are the
593    * persistent representation strings of the genes - components
594    *
595    * @author Audrius Meskauskas
596    */

597   protected static final List split(String JavaDoc a_string)
598       throws UnsupportedRepresentationException {
599     List a = Collections.synchronizedList(new ArrayList());
600     StringTokenizer st = new StringTokenizer
601         (a_string, GENE_DELIMITER_HEADING + GENE_DELIMITER_CLOSING, true);
602     while (st.hasMoreTokens()) {
603       if (!st.nextToken().equals(GENE_DELIMITER_HEADING)) {
604         throw new UnsupportedRepresentationException
605             (a_string + " no open tag");
606       }
607       String JavaDoc n = st.nextToken();
608       if (n.equals(GENE_DELIMITER_CLOSING)) a.add(""); // Empty token
609
else {
610         a.add(n);
611         if (!st.nextToken().equals(GENE_DELIMITER_CLOSING)) {
612           throw new UnsupportedRepresentationException
613               (a_string + " no close tag");
614         }
615       }
616     }
617     return a;
618   }
619
620   /** Append a new gene to the gene array. */
621   public void addGene(Gene a_gene) {
622     Gene[] genes = new Gene[m_genes.length + 1];
623     System.arraycopy(m_genes, 0, genes, 0, m_genes.length);
624     genes[m_genes.length] = a_gene;
625     m_genes = genes;
626   }
627
628   /**
629    * Sets an object, responsible for deciding if the Supergene allele
630    * combination is valid. If it is set to null, no validation is performed
631    * (all combinations are assumed to be valid). If no validator is
632    * set, the method <code>isValid (Gene [] ) </code>is called.
633    */

634   public void setValidator(SupergeneValidator a_validator) {
635     m_validator = a_validator;
636   }
637
638   /**
639    * Gets an object, responsible for deciding if the Supergene allele
640    * combination is valid. If no external validator was set and the
641    * class uses its own internal validation method, it returns <i>this</i>
642    */

643   public SupergeneValidator getValidator() {
644     return m_validator;
645   }
646
647   /** A validator (initially set to <i>this</i> */
648   protected SupergeneValidator m_validator = this;
649
650   /** {@inheritDoc}
651    * The default implementation returns an empty string. */

652   public String JavaDoc getPersistent() {
653     return "";
654   }
655
656   /** {@inheritDoc}
657    * The default implementation does nothing. */

658   public void setFromPersistent(String JavaDoc a_from) {
659   }
660
661   /**
662    * @return not needed for abstract supergene
663    */

664   public Object JavaDoc getInternalValue() {
665     if (true) {
666       throw new RuntimeException JavaDoc("getInternalValue() called unexpectedly!");
667     }
668     return null;
669   }
670 }
671
Popular Tags