RandomKey.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.math4.legacy.genetics;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.Comparator;
  22. import java.util.List;

  23. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  24. import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
  25. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;

  26. /**
  27.  * Random Key chromosome is used for permutation representation. It is a vector
  28.  * of a fixed length of real numbers in [0,1] interval. The index of the i-th
  29.  * smallest value in the vector represents an i-th member of the permutation.
  30.  * <p>
  31.  * For example, the random key [0.2, 0.3, 0.8, 0.1] corresponds to the
  32.  * permutation of indices (3,0,1,2). If the original (unpermuted) sequence would
  33.  * be (a,b,c,d), this would mean the sequence (d,a,b,c).
  34.  * <p>
  35.  * With this representation, common operators like n-point crossover can be
  36.  * used, because any such chromosome represents a valid permutation.
  37.  * <p>
  38.  * Since the chromosome (and thus its arrayRepresentation) is immutable, the
  39.  * array representation is sorted only once in the constructor.
  40.  * <p>
  41.  * For details, see:
  42.  * <ul>
  43.  *   <li>Bean, J.C.: Genetic algorithms and random keys for sequencing and
  44.  *       optimization. ORSA Journal on Computing 6 (1994) 154-160</li>
  45.  *   <li>Rothlauf, F.: Representations for Genetic and Evolutionary Algorithms.
  46.  *       Volume 104 of Studies in Fuzziness and Soft Computing. Physica-Verlag,
  47.  *       Heidelberg (2002)</li>
  48.  * </ul>
  49.  *
  50.  * @param <T> type of the permuted objects
  51.  * @since 2.0
  52.  */
  53. public abstract class RandomKey<T> extends AbstractListChromosome<Double> implements PermutationChromosome<T> {

  54.     /** Cache of sorted representation (unmodifiable). */
  55.     private final List<Double> sortedRepresentation;

  56.     /**
  57.      * Base sequence [0,1,...,n-1], permuted according to the representation (unmodifiable).
  58.      */
  59.     private final List<Integer> baseSeqPermutation;

  60.     /**
  61.      * Constructor.
  62.      *
  63.      * @param representation list of [0,1] values representing the permutation
  64.      * @throws InvalidRepresentationException iff the <code>representation</code> can not represent a valid chromosome
  65.      */
  66.     public RandomKey(final List<Double> representation) throws InvalidRepresentationException {
  67.         super(representation);
  68.         // store the sorted representation
  69.         List<Double> sortedRepr = new ArrayList<> (getRepresentation());
  70.         Collections.sort(sortedRepr);
  71.         sortedRepresentation = Collections.unmodifiableList(sortedRepr);
  72.         // store the permutation of [0,1,...,n-1] list for toString() and isSame() methods
  73.         baseSeqPermutation = Collections.unmodifiableList(
  74.             decodeGeneric(baseSequence(getLength()), getRepresentation(), sortedRepresentation)
  75.         );
  76.     }

  77.     /**
  78.      * Constructor.
  79.      *
  80.      * @param representation array of [0,1] values representing the permutation
  81.      * @throws InvalidRepresentationException iff the <code>representation</code> can not represent a valid chromosome
  82.      */
  83.     public RandomKey(final Double[] representation) throws InvalidRepresentationException {
  84.         this(Arrays.asList(representation));
  85.     }

  86.     /**
  87.      * {@inheritDoc}
  88.      */
  89.     @Override
  90.     public List<T> decode(final List<T> sequence) {
  91.         return decodeGeneric(sequence, getRepresentation(), sortedRepresentation);
  92.     }

  93.     /**
  94.      * Decodes a permutation represented by <code>representation</code> and
  95.      * returns a (generic) list with the permuted values.
  96.      *
  97.      * @param <S> generic type of the sequence values
  98.      * @param sequence the unpermuted sequence
  99.      * @param representation representation of the permutation ([0,1] vector)
  100.      * @param sortedRepr sorted <code>representation</code>
  101.      * @return list with the sequence values permuted according to the representation
  102.      * @throws DimensionMismatchException iff the length of the <code>sequence</code>,
  103.      *   <code>representation</code> or <code>sortedRepr</code> lists are not equal
  104.      */
  105.     private static <S> List<S> decodeGeneric(final List<S> sequence, List<Double> representation,
  106.                                              final List<Double> sortedRepr)
  107.         throws DimensionMismatchException {

  108.         int l = sequence.size();

  109.         // the size of the three lists must be equal
  110.         if (representation.size() != l) {
  111.             throw new DimensionMismatchException(representation.size(), l);
  112.         }
  113.         if (sortedRepr.size() != l) {
  114.             throw new DimensionMismatchException(sortedRepr.size(), l);
  115.         }

  116.         // do not modify the original representation
  117.         List<Double> reprCopy = new ArrayList<> (representation);

  118.         // now find the indices in the original repr and use them for permuting
  119.         List<S> res = new ArrayList<> (l);
  120.         for (int i=0; i<l; i++) {
  121.             int index = reprCopy.indexOf(sortedRepr.get(i));
  122.             res.add(sequence.get(index));
  123.             reprCopy.set(index, null);
  124.         }
  125.         return res;
  126.     }

  127.     /**
  128.      * Returns <code>true</code> iff <code>another</code> is a RandomKey and
  129.      * encodes the same permutation.
  130.      *
  131.      * @param another chromosome to compare
  132.      * @return true iff chromosomes encode the same permutation
  133.      */
  134.     @Override
  135.     protected boolean isSame(final Chromosome another) {
  136.         // type check
  137.         if (! (another instanceof RandomKey<?>)) {
  138.             return false;
  139.         }
  140.         RandomKey<?> anotherRk = (RandomKey<?>) another;
  141.         // size check
  142.         if (getLength() != anotherRk.getLength()) {
  143.             return false;
  144.         }

  145.         // two different representations can still encode the same permutation
  146.         // the ordering is what counts
  147.         List<Integer> thisPerm = this.baseSeqPermutation;
  148.         List<Integer> anotherPerm = anotherRk.baseSeqPermutation;

  149.         for (int i=0; i<getLength(); i++) {
  150.             if (!thisPerm.get(i).equals(anotherPerm.get(i))) {
  151.                 return false;
  152.             }
  153.         }
  154.         // the permutations are the same
  155.         return true;
  156.     }

  157.     /**
  158.      * {@inheritDoc}
  159.      */
  160.     @Override
  161.     protected void checkValidity(final List<Double> chromosomeRepresentation)
  162.         throws InvalidRepresentationException {

  163.         for (double val : chromosomeRepresentation) {
  164.             if (val < 0 || val > 1) {
  165.                 throw new InvalidRepresentationException(LocalizedFormats.OUT_OF_RANGE_SIMPLE,
  166.                                                          val, 0, 1);
  167.             }
  168.         }
  169.     }


  170.     /**
  171.      * Generates a representation corresponding to a random permutation of
  172.      * length l which can be passed to the RandomKey constructor.
  173.      *
  174.      * @param l length of the permutation
  175.      * @return representation of a random permutation
  176.      */
  177.     public static final List<Double> randomPermutation(final int l) {
  178.         List<Double> repr = new ArrayList<>(l);
  179.         for (int i=0; i<l; i++) {
  180.             repr.add(GeneticAlgorithm.getRandomGenerator().nextDouble());
  181.         }
  182.         return repr;
  183.     }

  184.     /**
  185.      * Generates a representation corresponding to an identity permutation of
  186.      * length l which can be passed to the RandomKey constructor.
  187.      *
  188.      * @param l length of the permutation
  189.      * @return representation of an identity permutation
  190.      */
  191.     public static final List<Double> identityPermutation(final int l) {
  192.         List<Double> repr = new ArrayList<>(l);
  193.         for (int i=0; i<l; i++) {
  194.             repr.add((double)i/l);
  195.         }
  196.         return repr;
  197.     }

  198.     /**
  199.      * Generates a representation of a permutation corresponding to the
  200.      * <code>data</code> sorted by <code>comparator</code>. The
  201.      * <code>data</code> is not modified during the process.
  202.      *
  203.      * This is useful if you want to inject some permutations to the initial
  204.      * population.
  205.      *
  206.      * @param <S> type of the data
  207.      * @param data list of data determining the order
  208.      * @param comparator how the data will be compared
  209.      * @return list representation of the permutation corresponding to the parameters
  210.      */
  211.     public static <S> List<Double> comparatorPermutation(final List<S> data,
  212.                                                          final Comparator<S> comparator) {
  213.         List<S> sortedData = new ArrayList<>(data);
  214.         Collections.sort(sortedData, comparator);

  215.         return inducedPermutation(data, sortedData);
  216.     }

  217.     /**
  218.      * Generates a representation of a permutation corresponding to a
  219.      * permutation which yields <code>permutedData</code> when applied to
  220.      * <code>originalData</code>.
  221.      *
  222.      * This method can be viewed as an inverse to {@link #decode(List)}.
  223.      *
  224.      * @param <S> type of the data
  225.      * @param originalData the original, unpermuted data
  226.      * @param permutedData the data, somehow permuted
  227.      * @return representation of a permutation corresponding to the permutation
  228.      *   {@code originalData -> permutedData}
  229.      * @throws DimensionMismatchException iff the length of <code>originalData</code>
  230.      *   and <code>permutedData</code> lists are not equal
  231.      * @throws MathIllegalArgumentException iff the <code>permutedData</code> and
  232.      *   <code>originalData</code> lists contain different data
  233.      */
  234.     public static <S> List<Double> inducedPermutation(final List<S> originalData,
  235.                                                       final List<S> permutedData)
  236.         throws DimensionMismatchException, MathIllegalArgumentException {

  237.         if (originalData.size() != permutedData.size()) {
  238.             throw new DimensionMismatchException(permutedData.size(), originalData.size());
  239.         }
  240.         int l = originalData.size();

  241.         List<S> origDataCopy = new ArrayList<> (originalData);

  242.         Double[] res = new Double[l];
  243.         for (int i=0; i<l; i++) {
  244.             int index = origDataCopy.indexOf(permutedData.get(i));
  245.             if (index == -1) {
  246.                 throw new MathIllegalArgumentException(LocalizedFormats.DIFFERENT_ORIG_AND_PERMUTED_DATA);
  247.             }
  248.             res[index] = (double) i / l;
  249.             origDataCopy.set(index, null);
  250.         }
  251.         return Arrays.asList(res);
  252.     }

  253.     /** {@inheritDoc} */
  254.     @Override
  255.     public String toString() {
  256.         return String.format("(f=%s pi=(%s))", getFitness(), baseSeqPermutation);
  257.     }

  258.     /**
  259.      * Helper for constructor. Generates a list of natural numbers (0,1,...,l-1).
  260.      *
  261.      * @param l length of list to generate
  262.      * @return list of integers from 0 to l-1
  263.      */
  264.     private static List<Integer> baseSequence(final int l) {
  265.         List<Integer> baseSequence = new ArrayList<> (l);
  266.         for (int i=0; i<l; i++) {
  267.             baseSequence.add(i);
  268.         }
  269.         return baseSequence;
  270.     }
  271. }