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 — <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 < 0 || 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 >= 0 && _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 }
|