001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.0). 003 * Copyright (c) 2007-2024 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@gmail.com) 019 */ 020package io.jenetics; 021 022import static java.lang.String.format; 023 024import io.jenetics.internal.math.Subsets; 025import io.jenetics.util.MSeq; 026import io.jenetics.util.RandomRegistry; 027 028/** 029 * The {@code PartiallyMatchedCrossover} (PMX) guarantees that all {@link Gene}s 030 * are found exactly once in each chromosome. No gene is duplicated by this 031 * crossover. The PMX can be applied usefully in the TSP or other permutation 032 * problem encodings. Permutation encoding is useful for all problems where the 033 * fitness only depends on the ordering of the genes within the chromosome. This 034 * is the case in many combinatorial optimization problems. Other crossover 035 * operators for combinatorial optimization are: 036 * <ul> 037 * <li>order crossover</li> 038 * <li>cycle crossover</li> 039 * <li>edge recombination crossover</li> 040 * <li>edge assembly crossover</li> 041 * </ul> 042 * <p> 043 * The PMX is similar to the two-point crossover. A crossing region is chosen 044 * by selecting two crossing points. 045 * <pre> 046 * C1 = 012|345|6789 047 * C2 = 987|654|3210 048 * </pre> 049 * After performing the crossover, we normally got two invalid chromosomes. 050 * <pre> 051 * C1 = 012|654|6789 052 * C2 = 987|345|3210 053 * </pre> 054 * Chromosome {@code C1} contains value 6 twice and misses value 055 * 3. On the other side chromosome {@code C2} contains the value 3 twice and 056 * misses value 6. We can observe that this crossover is equivalent 057 * to the exchange of the values {@code 3 -> 6}, {@code 4 -> 5} and 058 * {@code 5 -> 4}. To repair the two chromosomes, we have to apply this exchange 059 * outside the crossing region. 060 * <pre> 061 * C1 = 012|654|3789 062 * C2 = 987|345|6210 063 * </pre> 064 * 065 * <em>The {@code PartiallyMatchedCrossover} class requires chromosomes with the 066 * same length. An {@code IllegalArgumentException} is thrown at runtime if this 067 * requirement is not fulfilled.</em> 068 * 069 * @see PermutationChromosome 070 * 071 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 072 * @since 1.0 073 * @version 4.4 074 */ 075public class PartiallyMatchedCrossover<T, C extends Comparable<? super C>> 076 extends Crossover<EnumGene<T>, C> 077{ 078 079 public PartiallyMatchedCrossover(final double probability) { 080 super(probability); 081 } 082 083 @Override 084 protected int crossover( 085 final MSeq<EnumGene<T>> that, 086 final MSeq<EnumGene<T>> other 087 ) { 088 if (that.length() != other.length()) { 089 throw new IllegalArgumentException(format( 090 "Required chromosomes with same length: %s != %s", 091 that.length(), other.length() 092 )); 093 } 094 095 if (that.length() >= 2) { 096 final var random = RandomRegistry.random(); 097 final int[] points = Subsets.next(random, that.length(), 2); 098 099 that.swap(points[0], points[1], other, points[0]); 100 repair(that, other, points[0], points[1]); 101 repair(other, that, points[0], points[1]); 102 } 103 104 return 1; 105 } 106 107 private static <T> void repair( 108 final MSeq<T> that, final MSeq<T> other, 109 final int begin, final int end 110 ) { 111 for (int i = 0; i < begin; ++i) { 112 int index = that.indexOf(that.get(i), begin, end); 113 while (index != -1) { 114 that.set(i, other.get(index)); 115 index = that.indexOf(that.get(i), begin, end); 116 } 117 } 118 for (int i = end, n = that.length(); i < n; ++i) { 119 int index = that.indexOf(that.get(i), begin, end); 120 while (index != -1) { 121 that.set(i, other.get(index)); 122 index = that.indexOf(that.get(i), begin, end); 123 } 124 } 125 } 126 127 @Override 128 public String toString() { 129 return format("%s[p=%f]", getClass().getSimpleName(), _probability); 130 } 131 132}