All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jenetics.PermutationChromosome Maven / Gradle / Ivy

/*
 * Java Genetic Algorithm Library (jenetics-3.4.0).
 * Copyright (c) 2007-2016 Franz Wilhelmstötter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 *    Franz Wilhelmstötter ([email protected])
 */
package org.jenetics;

import static java.lang.String.format;
import static org.jenetics.internal.util.bit.getAndSet;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlList;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.jenetics.internal.math.base;
import org.jenetics.internal.util.Equality;
import org.jenetics.internal.util.Hash;
import org.jenetics.internal.util.array;
import org.jenetics.internal.util.bit;
import org.jenetics.internal.util.jaxb;
import org.jenetics.internal.util.require;

import org.jenetics.util.ISeq;
import org.jenetics.util.IntRange;
import org.jenetics.util.MSeq;
import org.jenetics.util.Seq;

/**
 * This chromosome can be used to model permutations of a given (sub) set of
 * alleles.
 *
 * 
{@code
 * final ISeq alleles = ISeq.of("one", "two", "three", "four", "five");
 *
 * // Create a new randomly permuted chromosome from the given alleles.
 * final PermutationChromosome ch1 = PermutationChromosome.of(alleles);
 * System.out.println(ch1);
 * System.out.println(ch1.newInstance());
 *
 * // Create a new randomly permuted chromosome from a subset of the given alleles.
 * final PermutationChromosome ch2 = PermutationChromosome.of(alleles, 3);
 * System.out.println(ch2);
 * System.out.println(ch2.newInstance());
 *
 * // Console output:
 * // > three|two|one|five|four
 * // > two|one|four|five|three
 * // > three|one|five
 * // > five|three|one
 * }
* * Usable {@link Alterer} for this chromosome: *
    *
  • {@link PartiallyMatchedCrossover}
  • *
  • {@link SwapMutator}
  • *
*

* Implementation note 1: * The factory methods of the {@link AbstractChromosome} has been overridden so * that no invalid permutation will be created. * * *

* Implementation note 2: * This class uses an algorithm for choosing subsets which is based on a * FORTRAN77 version, originally implemented by Albert Nijenhuis, Herbert Wilf. * The actual Java implementation is based on the C++ version by John Burkardt. * *
* * Reference: * Albert Nijenhuis, Herbert Wilf, * Combinatorial Algorithms for Computers and Calculators, * Second Edition, * Academic Press, 1978, * ISBN: 0-12-519260-6, * LC: QA164.N54. *

* * * @see PartiallyMatchedCrossover * @see SwapMutator * * @author Franz Wilhelmstötter * @since 1.0 * @version 3.4 */ @XmlJavaTypeAdapter(PermutationChromosome.Model.Adapter.class) public final class PermutationChromosome extends AbstractChromosome> implements Serializable { private static final long serialVersionUID = 2L; private ISeq _validAlleles; // Private primary constructor. private PermutationChromosome( final ISeq> genes, final Boolean valid ) { super(genes); assert !genes.isEmpty(); _validAlleles = genes.get(0).getValidAlleles(); _valid = valid; } /** * Create a new {@code PermutationChromosome} from the given {@code genes}. * If the given {@code genes} sequence contains duplicate entries, the * created {@code PermutationChromosome} will be invalid * ({@code ch.isValid() == false}). * * @param genes the enum genes the new chromosome consists of * @throws NullPointerException if the given {@code genes} are null * @throws IllegalArgumentException if the given {@code genes} sequence is * empty */ public PermutationChromosome(final ISeq> genes) { this(genes, null); } /** * Return the sequence of valid alleles of this chromosome. * * @return the sequence of valid alleles of this chromosome */ public ISeq getValidAlleles() { return _validAlleles; } /** * Check if this chromosome represents still a valid permutation (or subset) * of the given valid alleles. */ @Override public boolean isValid() { if (_valid == null) { final byte[] check = bit.newArray(_validAlleles.length()); _valid = _genes.forAll(g -> !getAndSet(check, g.getAlleleIndex())); } return _valid; } /** * Create a new, random chromosome. */ @Override public PermutationChromosome newInstance() { return of(_validAlleles, length()); } @Override public PermutationChromosome newInstance(final ISeq> genes) { return new PermutationChromosome<>(genes); } @Override public int hashCode() { return Hash.of(getClass()) .and(super.hashCode()) .value(); } @Override public boolean equals(final Object obj) { return Equality.of(this, obj).test(super::equals); } @Override public String toString() { return _genes.asList().stream() .map(g -> g.getAllele().toString()) .collect(Collectors.joining("|")); } /** * Create a new, random chromosome with the given valid alleles and the * desired length. *

* The following example shows how to create a {@code PermutationChromosome} * for encoding a sub-set problem (of a fixed {@code length}). *

{@code
	 * final ISeq basicSet = ISeq.of("a", "b", "c", "d", "e", "f");
	 *
	 * // The chromosome has a length of 3 and will only contain values from the
	 * // given basic-set, with no duplicates.
	 * final PermutationChromosome ch = PermutationChromosome.of(basicSet, 3);
	 * }
* * @since 3.4 * * @param alleles the base-set of the valid alleles * @param length the length of the created chromosomes * @param the allele type * @return a new chromosome with the given valid alleles and the desired * length * @throws IllegalArgumentException if {@code alleles.size() < length}, * {@code length <= 0} or {@code alleles.size()*length} will * cause an integer overflow. * @throws NullPointerException if one of the arguments is {@code null} */ public static PermutationChromosome of( final ISeq alleles, final int length ) { require.positive(length); if (length > alleles.size()) { throw new IllegalArgumentException(format( "The sub-set size must be be greater then the base-set: %d > %d", length, alleles.size() )); } final int[] subset = array.shuffle(base.subset(alleles.size(), length)); return new PermutationChromosome<>( IntStream.of(subset) .mapToObj(i -> EnumGene.of(i, alleles)) .collect(ISeq.toISeq()), true ); } /** * Create a new, random chromosome with the given valid alleles. * * @param the gene type of the chromosome * @param alleles the valid alleles used for this permutation arrays. * @return a new chromosome with the given alleles * @throws IllegalArgumentException if the given allele sequence is empty. */ public static PermutationChromosome of(final ISeq alleles) { return of(alleles, alleles.size()); } /** * Create a new, random chromosome with the given valid alleles. * * @since 2.0 * * @param the gene type of the chromosome * @param alleles the valid alleles used for this permutation arrays. * @return a new chromosome with the given alleles * @throws IllegalArgumentException if the given allele array is empty. * @throws NullPointerException if one of the alleles is {@code null} */ @SafeVarargs public static PermutationChromosome of(final T... alleles) { return of(ISeq.of(alleles)); } /** * Create a integer permutation chromosome with the given length. * * @param length the chromosome length. * @return a integer permutation chromosome with the given length. * @throws IllegalArgumentException if {@code length <= 0}. */ public static PermutationChromosome ofInteger(final int length) { return ofInteger(0, require.positive(length)); } /** * Create an integer permutation chromosome with the given range. * * @since 2.0 * * @param start the start of the integer range (inclusively) of the returned * chromosome. * @param end the end of the integer range (exclusively) of the returned * chromosome. * @return a integer permutation chromosome with the given integer range * values. * @throws IllegalArgumentException if {@code start >= end} or * {@code start <= 0} */ public static PermutationChromosome ofInteger(final int start, final int end) { if (end <= start) { throw new IllegalArgumentException(format( "end <= start: %d <= %d", end, start )); } return ofInteger(IntRange.of(start, end), end - start); } /** * Create an integer permutation chromosome with the given range and length * * @since 3.4 * * @param range the value range * @param length the chromosome length * @return a new integer permutation chromosome * @throws NullPointerException if the given {@code range} is {@code null} * @throws IllegalArgumentException if * {@code range.getMax() - range.getMin() < length}, * {@code length <= 0} or * {@code (range.getMax() - range.getMin())*length} will cause an * integer overflow. */ public static PermutationChromosome ofInteger(final IntRange range, final int length) { return of( range.stream() .mapToObj(i -> i) .collect(ISeq.toISeq()), length ); } /* ************************************************************************* * Java object serialization * ************************************************************************/ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(_validAlleles); for (EnumGene gene : _genes) { out.writeInt(gene.getAlleleIndex()); } } @SuppressWarnings("unchecked") private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); _validAlleles = (ISeq)in.readObject(); final MSeq> genes = MSeq.ofLength(_validAlleles.length()); for (int i = 0; i < _validAlleles.length(); ++i) { genes.set(i, new EnumGene<>(in.readInt(), _validAlleles)); } _genes = genes.toISeq(); } /* ************************************************************************* * JAXB object serialization * ************************************************************************/ @XmlRootElement(name = "permutation-chromosome") @XmlType(name = "org.jenetics.PermutationChromosome") @XmlAccessorType(XmlAccessType.FIELD) @SuppressWarnings({"unchecked", "rawtypes"}) static final class Model { @XmlAttribute public int length; @XmlElementWrapper(name = "valid-alleles") @XmlElement(name = "allele") public List alleles; @XmlList @XmlElement(name = "order") public List order; public static final class Adapter extends XmlAdapter { @Override public Model marshal(final PermutationChromosome pc) throws Exception { final Model model = new Model(); model.length = pc.length(); model.alleles = pc.getValidAlleles() .map(jaxb.Marshaller(pc.getValidAlleles().get(0))) .asList(); model.order = ((Seq>)pc.toSeq()) .map(EnumGene::getAlleleIndex) .asList(); return model; } @Override public PermutationChromosome unmarshal(final Model model) throws Exception { final ISeq alleles = ISeq.of(model.alleles) .map(jaxb.Unmarshaller(model.alleles.get(0))); return new PermutationChromosome( ISeq.of(model.order).map(Gene(alleles)) ); } } private static Function> Gene(final ISeq alleles) { return value -> new EnumGene<>(value, alleles); } } }