PermutationChromosome.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-3.7.0).
003  * Copyright (c) 2007-2016 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.bit.getAndSet;
024 
025 import java.io.IOException;
026 import java.io.ObjectInputStream;
027 import java.io.ObjectOutputStream;
028 import java.io.Serializable;
029 import java.util.List;
030 import java.util.function.Function;
031 import java.util.stream.Collectors;
032 import java.util.stream.IntStream;
033 
034 import javax.xml.bind.annotation.XmlAccessType;
035 import javax.xml.bind.annotation.XmlAccessorType;
036 import javax.xml.bind.annotation.XmlAttribute;
037 import javax.xml.bind.annotation.XmlElement;
038 import javax.xml.bind.annotation.XmlElementWrapper;
039 import javax.xml.bind.annotation.XmlList;
040 import javax.xml.bind.annotation.XmlRootElement;
041 import javax.xml.bind.annotation.XmlType;
042 import javax.xml.bind.annotation.adapters.XmlAdapter;
043 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
044 
045 import org.jenetics.internal.math.base;
046 import org.jenetics.internal.util.Equality;
047 import org.jenetics.internal.util.Hash;
048 import org.jenetics.internal.util.array;
049 import org.jenetics.internal.util.bit;
050 import org.jenetics.internal.util.jaxb;
051 import org.jenetics.internal.util.reflect;
052 import org.jenetics.internal.util.require;
053 
054 import org.jenetics.util.ISeq;
055 import org.jenetics.util.IntRange;
056 import org.jenetics.util.MSeq;
057 import org.jenetics.util.Seq;
058 
059 /**
060  * This chromosome can be used to model permutations of a given (sub) set of
061  * alleles.
062  *
063  <pre>{@code
064  * final ISeq<String> alleles = ISeq.of("one", "two", "three", "four", "five");
065  *
066  * // Create a new randomly permuted chromosome from the given alleles.
067  * final PermutationChromosome<String> ch1 = PermutationChromosome.of(alleles);
068  * System.out.println(ch1);
069  * System.out.println(ch1.newInstance());
070  *
071  * // Create a new randomly permuted chromosome from a subset of the given alleles.
072  * final PermutationChromosome<String> ch2 = PermutationChromosome.of(alleles, 3);
073  * System.out.println(ch2);
074  * System.out.println(ch2.newInstance());
075  *
076  * // Console output:
077  * // > three|two|one|five|four
078  * // > two|one|four|five|three
079  * // > three|one|five
080  * // > five|three|one
081  * }</pre>
082  *
083  * Usable {@link Alterer} for this chromosome:
084  <ul>
085  *     <li>{@link PartiallyMatchedCrossover}</li>
086  *     <li>{@link SwapMutator}</li>
087  </ul>
088  <p>
089  <em><b>Implementation note 1:</b>
090  * The factory methods of the {@link AbstractChromosome} has been overridden so
091  * that no invalid permutation will be created.
092  </em>
093  *
094  <p>
095  <em><b>Implementation note 2:</b>
096  * This class uses an algorithm for choosing subsets which is based on a
097  * FORTRAN77 version, originally implemented by Albert Nijenhuis, Herbert Wilf.
098  * The actual Java implementation is based on the  C++ version by John Burkardt.
099  </em>
100  <br>
101  <em><a href="https://people.sc.fsu.edu/~burkardt/c_src/subset/subset.html">
102  *  Reference:</a></em>
103  *   Albert Nijenhuis, Herbert Wilf,
104  *   Combinatorial Algorithms for Computers and Calculators,
105  *   Second Edition,
106  *   Academic Press, 1978,
107  *   ISBN: 0-12-519260-6,
108  *   LC: QA164.N54.
109  </p>
110  *
111  *
112  @see PartiallyMatchedCrossover
113  @see SwapMutator
114  *
115  @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
116  @since 1.0
117  @version 3.4
118  */
119 @XmlJavaTypeAdapter(PermutationChromosome.Model.Adapter.class)
120 public final class PermutationChromosome<T>
121     extends AbstractChromosome<EnumGene<T>>
122     implements Serializable
123 {
124     private static final long serialVersionUID = 2L;
125 
126     private ISeq<T> _validAlleles;
127 
128     // Private primary constructor.
129     private PermutationChromosome(
130         final ISeq<EnumGene<T>> genes,
131         final Boolean valid
132     ) {
133         super(genes);
134 
135         assert !genes.isEmpty();
136         _validAlleles = genes.get(0).getValidAlleles();
137         _valid = valid;
138     }
139 
140     /**
141      * Create a new {@code PermutationChromosome} from the given {@code genes}.
142      * If the given {@code genes} sequence contains duplicate entries, the
143      * created {@code PermutationChromosome} will be invalid
144      * ({@code ch.isValid() == false}).
145      *
146      @param genes the enum genes the new chromosome consists of
147      @throws NullPointerException if the given {@code genes} are null
148      @throws IllegalArgumentException if the given {@code genes} sequence is
149      *         empty
150      */
151     public PermutationChromosome(final ISeq<EnumGene<T>> genes) {
152         this(genes, null);
153     }
154 
155     /**
156      * Return the sequence of valid alleles of this chromosome.
157      *
158      @return the sequence of valid alleles of this chromosome
159      */
160     public ISeq<T> getValidAlleles() {
161         return _validAlleles;
162     }
163 
164     /**
165      * Check if this chromosome represents still a valid permutation (or subset)
166      * of the given valid alleles.
167      */
168     @Override
169     public boolean isValid() {
170         if (_valid == null) {
171             final byte[] check = bit.newArray(_validAlleles.length());
172             _valid = _genes.forAll(g -> !getAndSet(check, g.getAlleleIndex()));
173         }
174 
175         return _valid;
176     }
177 
178     /**
179      * Create a new, <em>random</em> chromosome.
180      */
181     @Override
182     public PermutationChromosome<T> newInstance() {
183         return of(_validAlleles, length());
184     }
185 
186     @Override
187     public PermutationChromosome<T> newInstance(final ISeq<EnumGene<T>> genes) {
188         return new PermutationChromosome<>(genes);
189     }
190 
191     @Override
192     public int hashCode() {
193         return Hash.of(getClass())
194                 .and(super.hashCode())
195                 .value();
196     }
197 
198     @Override
199     public boolean equals(final Object obj) {
200         return Equality.of(this, obj).test(super::equals);
201     }
202 
203     @Override
204     public String toString() {
205         return _genes.asList().stream()
206             .map(g -> g.getAllele().toString())
207             .collect(Collectors.joining("|"));
208     }
209 
210     /**
211      * Create a new, random chromosome with the given valid alleles and the
212      * desired length.
213      <p>
214      * The following example shows how to create a {@code PermutationChromosome}
215      * for encoding a sub-set problem (of a fixed {@code length}).
216      <pre>{@code
217      * final ISeq<String> basicSet = ISeq.of("a", "b", "c", "d", "e", "f");
218      *
219      * // The chromosome has a length of 3 and will only contain values from the
220      * // given basic-set, with no duplicates.
221      * final PermutationChromosome<String> ch = PermutationChromosome.of(basicSet, 3);
222      * }</pre>
223      *
224      @since 3.4
225      *
226      @param alleles the base-set of the valid alleles
227      @param length the length of the created chromosomes
228      @param <T> the allele type
229      @return a new chromosome with the given valid alleles and the desired
230      *         length
231      @throws IllegalArgumentException if {@code alleles.size() < length},
232      *         {@code length <= 0} or {@code alleles.size()*length} will
233      *         cause an integer overflow.
234      @throws NullPointerException if one of the arguments is {@code null}
235      */
236     public static <T> PermutationChromosome<T> of(
237         final ISeq<? extends T> alleles,
238         final int length
239     ) {
240         require.positive(length);
241         if (length > alleles.size()) {
242             throw new IllegalArgumentException(format(
243                 "The sub-set size must be be greater then the base-set: %d > %d",
244                 length, alleles.size()
245             ));
246         }
247 
248         final int[] subset = array.shuffle(base.subset(alleles.size(), length));
249         final ISeq<EnumGene<T>> genes = IntStream.of(subset)
250             .mapToObj(i -> EnumGene.<T>of(i, alleles))
251             .collect(ISeq.toISeq());
252 
253         return new PermutationChromosome<>(genes, true);
254     }
255 
256     /**
257      * Create a new, random chromosome with the given valid alleles.
258      *
259      @param <T> the gene type of the chromosome
260      @param alleles the valid alleles used for this permutation arrays.
261      @return a new chromosome with the given alleles
262      @throws IllegalArgumentException if the given allele sequence is empty.
263      */
264     public static <T> PermutationChromosome<T>
265     of(final ISeq<? extends T> alleles) {
266         return of(alleles, alleles.size());
267     }
268 
269     /**
270      * Create a new, random chromosome with the given valid alleles.
271      *
272      @since 2.0
273      *
274      @param <T> the gene type of the chromosome
275      @param alleles the valid alleles used for this permutation arrays.
276      @return a new chromosome with the given alleles
277      @throws IllegalArgumentException if the given allele array is empty.
278      @throws NullPointerException if one of the alleles is {@code null}
279      */
280     @SafeVarargs
281     public static <T> PermutationChromosome<T> of(final T... alleles) {
282         return of(ISeq.of(alleles));
283     }
284 
285     /**
286      * Create a integer permutation chromosome with the given length.
287      *
288      @param length the chromosome length.
289      @return a integer permutation chromosome with the given length.
290      @throws IllegalArgumentException if {@code length <= 0}.
291      */
292     public static PermutationChromosome<Integer> ofInteger(final int length) {
293         return ofInteger(0, require.positive(length));
294     }
295 
296     /**
297      * Create an integer permutation chromosome with the given range.
298      *
299      @since 2.0
300      *
301      @param start the start of the integer range (inclusively) of the returned
302      *        chromosome.
303      @param end the end of the integer range (exclusively) of the returned
304      *        chromosome.
305      @return a integer permutation chromosome with the given integer range
306      *         values.
307      @throws IllegalArgumentException if {@code start >= end} or
308      *         {@code start <= 0}
309      */
310     public static PermutationChromosome<Integer>
311     ofInteger(final int start, final int end) {
312         if (end <= start) {
313             throw new IllegalArgumentException(format(
314                 "end <= start: %d <= %d", end, start
315             ));
316         }
317 
318         return ofInteger(IntRange.of(start, end), end - start);
319     }
320 
321     /**
322      * Create an integer permutation chromosome with the given range and length
323      *
324      @since 3.4
325      *
326      @param range the value range
327      @param length the chromosome length
328      @return a new integer permutation chromosome
329      @throws NullPointerException if the given {@code range} is {@code null}
330      @throws IllegalArgumentException if
331      *         {@code range.getMax() - range.getMin() < length},
332      *         {@code length <= 0} or
333      *         {@code (range.getMax() - range.getMin())*length} will cause an
334      *         integer overflow.
335      */
336     public static PermutationChromosome<Integer>
337     ofInteger(final IntRange range, final int length) {
338         return of(
339             range.stream()
340                 .mapToObj(i -> i)
341                 .collect(ISeq.toISeq()),
342             length
343         );
344     }
345 
346     /* *************************************************************************
347      *  Java object serialization
348      * ************************************************************************/
349 
350     private void writeObject(final ObjectOutputStream out)
351         throws IOException
352     {
353         out.defaultWriteObject();
354 
355         out.writeObject(_validAlleles);
356         for (EnumGene<?> gene : _genes) {
357             out.writeInt(gene.getAlleleIndex());
358         }
359     }
360 
361     @SuppressWarnings("unchecked")
362     private void readObject(final ObjectInputStream in)
363         throws IOException, ClassNotFoundException
364     {
365         in.defaultReadObject();
366 
367         _validAlleles = (ISeq<T>)in.readObject();
368 
369         final MSeq<EnumGene<T>> genes = MSeq.ofLength(_validAlleles.length());
370         for (int i = 0; i < _validAlleles.length(); ++i) {
371             genes.set(i, new EnumGene<>(in.readInt(), _validAlleles));
372         }
373 
374         _genes = genes.toISeq();
375     }
376 
377     /* *************************************************************************
378      *  JAXB object serialization
379      * ************************************************************************/
380 
381     @XmlRootElement(name = "permutation-chromosome")
382     @XmlType(name = "org.jenetics.PermutationChromosome")
383     @XmlAccessorType(XmlAccessType.FIELD)
384     @SuppressWarnings({"unchecked""rawtypes"})
385     static final class Model {
386 
387         @XmlAttribute
388         public int length;
389 
390         @XmlElementWrapper(name = "valid-alleles")
391         @XmlElement(name = "allele")
392         public List<Object> alleles;
393 
394         @XmlList
395         @XmlElement(name = "order")
396         public List<Integer> order;
397 
398         public static final class Adapter
399             extends XmlAdapter<Model, PermutationChromosome> {
400             @Override
401             public Model marshal(final PermutationChromosome pc)
402                 throws Exception {
403                 final Model model = new Model();
404                 model.length = pc.length();
405                 model.alleles = pc.getValidAlleles()
406                     .map(jaxb.Marshaller(pc.getValidAlleles().get(0)))
407                     .asList();
408                 model.order = ((Seq<EnumGene<?>>)pc.toSeq())
409                     .map(EnumGene::getAlleleIndex)
410                     .asList();
411 
412                 return model;
413             }
414 
415             @Override
416             public PermutationChromosome unmarshal(final Model model)
417                 throws Exception {
418                 final ISeq alleles = ISeq.of(model.alleles)
419                     .map(jaxb.Unmarshaller(model.alleles.get(0)));
420 
421                 return new PermutationChromosome(
422                     ISeq.of(model.order).map(Gene(alleles))
423                 );
424             }
425         }
426 
427         private static Function<Integer, EnumGene<Object>>
428         Gene(final ISeq<Object> alleles) {
429             return value -> new EnumGene<>(value, alleles);
430         }
431     }
432 }