EnumGene.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-1.6.0).
003  * Copyright (c) 2007-2014 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
019  */
020 package org.jenetics;
021 
022 import static java.lang.String.format;
023 import static org.jenetics.internal.util.jaxb.Unmarshaller;
024 import static org.jenetics.internal.util.object.eq;
025 
026 import java.util.List;
027 import java.util.Objects;
028 
029 import javax.xml.bind.annotation.XmlAccessType;
030 import javax.xml.bind.annotation.XmlAccessorType;
031 import javax.xml.bind.annotation.XmlAnyElement;
032 import javax.xml.bind.annotation.XmlAttribute;
033 import javax.xml.bind.annotation.XmlRootElement;
034 import javax.xml.bind.annotation.XmlType;
035 import javax.xml.bind.annotation.adapters.XmlAdapter;
036 
037 import javolution.xml.XMLFormat;
038 import javolution.xml.stream.XMLStreamException;
039 
040 import org.jenetics.internal.util.HashBuilder;
041 import org.jenetics.internal.util.cast;
042 import org.jenetics.internal.util.jaxb;
043 import org.jenetics.internal.util.model.ModelType;
044 import org.jenetics.internal.util.model.ValueType;
045 
046 import org.jenetics.util.Array;
047 import org.jenetics.util.Factory;
048 import org.jenetics.util.Function;
049 import org.jenetics.util.ISeq;
050 import org.jenetics.util.RandomRegistry;
051 
052 /**
053  <p>
054  * Gene which holds enumerable (countable) genes. Will be used for combinatorial
055  * problems in combination with the {@link PermutationChromosome}.
056  </p>
057  * The following code shows how to create a combinatorial genotype factory which
058  * can be used when creating an {@link GeneticAlgorithm} instance.
059  * [code]
060  * final ISeq〈Integer〉 alleles = Array.box(1, 2, 3, 4, 5, 6, 7, 8).toISeq();
061  * final Factory〈Genotype〈EnumGene〈Integer〉〉〉 gtf = Genotype.of(
062  *     new PermutationChromosome<>(alleles)
063  * );
064  * [/code]
065  *
066  * The following code shows the assurances of the {@code EnumGene}.
067  * [code]
068  * final ISeq〈Integer〉 alleles = Array.box(1, 2, 3, 4, 5, 6, 7, 8).toISeq();
069  * final EnumGene〈Integer〉 gene = new EnumGene<>(5, alleles);
070  *
071  * assert(gene.getAlleleIndex() == 5);
072  * assert(gene.getAllele() == gene.getValidAlleles().get(5));
073  * assert(gene.getValidAlleles() == alleles);
074  * [/code]
075  *
076  @see PermutationChromosome
077  *
078  @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
079  @since 1.0
080  @version 1.6 &mdash; <em>$Date: 2014-03-06 $</em>
081  */
082 public final class EnumGene<A>
083     implements
084         Gene<A, EnumGene<A>>,
085         Comparable<EnumGene<A>>
086 {
087 
088     private static final long serialVersionUID = 1L;
089 
090     private final ISeq<A> _validAlleles;
091     private final int _alleleIndex;
092 
093     /**
094      * Create a new enum gene from the given valid genes and the chosen allele
095      * index.
096      @param alleleIndex the index of the allele for this gene.
097      @param validAlleles the sequence of valid alleles.
098      @throws java.lang.IllegalArgumentException if the give valid alleles
099      *         sequence is empty
100      @throws NullPointerException if the valid alleles seq is {@code null}.
101      */
102     public EnumGene(final int alleleIndex, final ISeq<? extends A> validAlleles) {
103         if (validAlleles.length() == 0) {
104             throw new IllegalArgumentException(
105                 "Array of valid alleles must be greater than zero."
106             );
107         }
108 
109         if (alleleIndex < || alleleIndex >= validAlleles.length()) {
110             throw new IndexOutOfBoundsException(format(
111                 "Allele index is not in range [0, %d).", alleleIndex
112             ));
113         }
114 
115         _validAlleles = cast.apply(validAlleles);
116         _alleleIndex = alleleIndex;
117     }
118 
119     /**
120      * Return sequence of the valid alleles where this gene is a part of.
121      *
122      @return the sequence of the valid alleles.
123      */
124     public ISeq<A> getValidAlleles() {
125         return _validAlleles;
126     }
127 
128     /**
129      * Return the index of the allele this gene is representing.
130      *
131      @return the index of the allele this gene is representing.
132      */
133     public int getAlleleIndex() {
134         return _alleleIndex;
135     }
136 
137     @Override
138     public A getAllele() {
139         return _validAlleles.get(_alleleIndex);
140     }
141 
142     @Deprecated
143     @Override
144     public EnumGene<A> copy() {
145         return new EnumGene<>(_alleleIndex, _validAlleles);
146     }
147 
148     @Override
149     public boolean isValid() {
150         return _alleleIndex >= && _alleleIndex < _validAlleles.length();
151     }
152 
153     @Override
154     public EnumGene<A> newInstance() {
155         return new EnumGene<>(
156             RandomRegistry.getRandom().nextInt(_validAlleles.length()),
157             _validAlleles
158         );
159     }
160 
161     /**
162      * Create a new gene from the given {@code value} and the gene context.
163      *
164      @since 1.6
165      @param value the value of the new gene.
166      @return a new gene with the given value.
167      */
168     public EnumGene<A> newInstance(final A value) {
169         return new EnumGene<>(
170             _validAlleles.indexOf(value),
171             _validAlleles
172         );
173     }
174 
175     @Override
176     public int compareTo(final EnumGene<A> gene) {
177         int result = 0;
178         if (_alleleIndex > gene._alleleIndex) {
179             result = 1;
180         else if (_alleleIndex < gene._alleleIndex) {
181             result = -1;
182         }
183 
184         return result;
185     }
186 
187     /**
188      @deprecated No longer needed after adding new factory methods to the
189      *             {@link Array} class.
190      */
191     @Deprecated
192     public Factory<EnumGene<A>> asFactory() {
193         return this;
194     }
195 
196     @Override
197     public int hashCode() {
198         return HashBuilder.of(EnumGene.class)
199                 .and(_alleleIndex)
200                 .and(_validAlleles).value();
201     }
202 
203     @Override
204     public boolean equals(final Object obj) {
205         if (obj == this) {
206             return true;
207         }
208         if (obj == null || getClass() != obj.getClass()) {
209             return false;
210         }
211 
212         final EnumGene<?> pg = (EnumGene<?>)obj;
213         return eq(_alleleIndex, pg._alleleIndex&&
214                 eq(_validAlleles, pg._validAlleles);
215     }
216 
217     @Override
218     public String toString() {
219         return Objects.toString(getAllele());
220     }
221 
222     /* *************************************************************************
223      *  Static object creation methods
224      * ************************************************************************/
225 
226     static <T> Function<Integer, EnumGene<T>> ToGene(
227         final ISeq<T> validAlleles
228     ) {
229         return new Function<Integer, EnumGene<T>>() {
230             @Override
231             public EnumGene<T> apply(final Integer index) {
232                 return new EnumGene<>(index, validAlleles);
233             }
234         };
235     }
236 
237     static <T> Factory<EnumGene<T>> Gene(final ISeq<? extends T> validAlleles) {
238         return new Factory<EnumGene<T>>() {
239             private int _index = 0;
240             @Override
241             public EnumGene<T> newInstance() {
242                 return new EnumGene<>(_index++, validAlleles);
243             }
244         };
245     }
246 
247     /**
248      * Return a new enum gene with an allele randomly chosen from the given
249      * valid alleles.
250      *
251      @param validAlleles the sequence of valid alleles.
252      @throws java.lang.IllegalArgumentException if the give valid alleles
253      *         sequence is empty
254      @throws NullPointerException if the valid alleles seq is {@code null}.
255      */
256     public static <A> EnumGene<A> of(final ISeq<? extends A> validAlleles) {
257         return new EnumGene<>(
258             RandomRegistry.getRandom().nextInt(validAlleles.length()),
259             validAlleles
260         );
261     }
262 
263     /**
264      @deprecated Use {@link #EnumGene(int, org.jenetics.util.ISeq)} instead.
265      */
266     @Deprecated
267     public static <A> EnumGene<A> valueOf(
268         final ISeq<? extends A> validAlleles,
269         final int alleleIndex
270     ) {
271         return new EnumGene<>(alleleIndex, validAlleles);
272     }
273 
274     /**
275      * Create a new enum gene from the given valid genes and the chosen allele
276      * index.
277      @param alleleIndex the index of the allele for this gene.
278      @param validAlleles the array of valid alleles.
279      *
280      @return a new enum gene
281      @throws java.lang.IllegalArgumentException if the give valid alleles
282      *         array is empty of the allele index is out of range.
283      */
284     @SafeVarargs
285     public static <G> EnumGene<G> of(
286         final int alleleIndex,
287         final G... validAlleles
288     ) {
289         return new EnumGene<>(alleleIndex, Array.of(validAlleles).toISeq());
290     }
291 
292     /**
293      @deprecated Use {@link #of(int, Object[])} instead.
294      */
295     @Deprecated
296     public static <G> EnumGene<G> valueOf(
297         final G[] validAlleles,
298         final int alleleIndex
299     ) {
300         return of(alleleIndex, validAlleles);
301     }
302 
303     /**
304      @deprecated Use {@link #of(org.jenetics.util.ISeq)} instead.
305      */
306     @Deprecated
307     public static <G> EnumGene<G> valueOf(final ISeq<G> validAlleles) {
308         return EnumGene.of(validAlleles);
309     }
310 
311     /**
312      * Return a new enum gene with an allele randomly chosen from the given
313      * valid alleles.
314      *
315      @param validAlleles the array of valid alleles.
316      @return a new enum gene
317      @throws java.lang.IllegalArgumentException if the give valid alleles
318      *         array is empty
319      */
320     @SafeVarargs
321     public static <G> EnumGene<G> of(final G... validAlleles) {
322         return EnumGene.of(Array.of(validAlleles).toISeq());
323     }
324 
325     /**
326      @deprecated Use {@link #of(Object[])} instead.
327      */
328     @Deprecated
329     public static <G> EnumGene<G> valueOf(final G[] validAlleles) {
330         return of(validAlleles);
331     }
332 
333     /* *************************************************************************
334      *  XML object serialization
335      * ************************************************************************/
336 
337     @SuppressWarnings("rawtypes")
338     static final XMLFormat<EnumGene>
339         XML = new XMLFormat<EnumGene>(EnumGene.class)
340     {
341         private static final String LENGTH = "length";
342         private static final String CURRENT_ALLELE_INDEX = "allele-index";
343 
344         @Override
345         public EnumGene newInstance(
346             final Class<EnumGene> cls, final InputElement xml
347         )
348             throws XMLStreamException
349         {
350             final int length = xml.getAttribute(LENGTH, 0);
351             final int index = xml.getAttribute(CURRENT_ALLELE_INDEX, 0);
352             final Array<Object> alleles = new Array<>(length);
353             for (int i = 0; i < length; ++i) {
354                 final Object allele = xml.getNext();
355                 alleles.set(i, allele);
356             }
357 
358             return new EnumGene<>(index, alleles.toISeq());
359         }
360 
361         @Override
362         public void write(final EnumGene eg, final OutputElement xml)
363             throws XMLStreamException
364         {
365             xml.setAttribute(LENGTH, eg.getValidAlleles().length());
366             xml.setAttribute(CURRENT_ALLELE_INDEX, eg.getAlleleIndex());
367             for (Object allele : eg.getValidAlleles()) {
368                 xml.add(allele);
369             }
370         }
371 
372         @Override
373         public void read(final InputElement xml, final EnumGene eg) {
374         }
375     };
376 
377     /* *************************************************************************
378      *  JAXB object serialization
379      * ************************************************************************/
380 
381     @XmlRootElement(name = "org.jenetics.EnumGene")
382     @XmlType(name = "org.jenetics.EnumGene")
383     @XmlAccessorType(XmlAccessType.FIELD)
384     @SuppressWarnings({"unchecked""rawtypes"})
385     final static class Model {
386         @XmlAttribute
387         int length;
388 
389         @XmlAttribute(name = "allele-index")
390         int currentAlleleIndex;
391 
392         @XmlAnyElement
393         List<Object> alleles;
394 
395         @ValueType(EnumGene.class)
396         @ModelType(Model.class)
397         public static final class Adapter
398             extends XmlAdapter<Model, EnumGene>
399         {
400             @Override
401             public Model marshal(final EnumGene value) {
402                 final Model m = new Model();
403                 m.length = value.getValidAlleles().length();
404                 m.currentAlleleIndex = value.getAlleleIndex();
405                 m.alleles = value.getValidAlleles()
406                     .map(jaxb.Marshaller(value.getValidAlleles().get(0))).asList();
407                 return m;
408             }
409 
410             @Override
411             public EnumGene unmarshal(final Model m) {
412                 return new EnumGene<>(
413                     m.currentAlleleIndex,
414                     Array.of(m.alleles).map(Unmarshaller).toISeq()
415                 );
416             }
417 
418         }
419     }
420 }