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.Math.min; 023import static java.lang.String.format; 024 025import io.jenetics.internal.util.Requires; 026import io.jenetics.util.MSeq; 027import io.jenetics.util.RandomRegistry; 028 029/** 030 * This alterer takes two chromosomes (treating it as vectors) and creates a 031 * linear combination of these vectors as a result. The line-recombination depends 032 * on a variable <em>p</em> which determines how far out along the line (defined 033 * by the two multidimensional points/vectors) the children are allowed to be. 034 * If <em>p</em> = 0 then the children will be located along the line within the 035 * hypercube between the two points. If <em>p</em> > 0 then the children may 036 * be located anywhere on the line, even somewhat outside the hypercube. 037 * <p> 038 * Points outside the allowed numeric range are rejected and the original 039 * value is used instead. The strategy on how out-of-range points are handled, 040 * is the difference to the very similar {@link IntermediateCrossover}. 041 * 042 * @see <a href="https://cs.gmu.edu/~sean/book/metaheuristics/"><em> 043 * Essentials of Metaheuristic, page 42</em></a> 044 * @see IntermediateCrossover 045 * 046 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 047 * @version 3.8 048 * @since 3.8 049 */ 050public class LineCrossover< 051 G extends NumericGene<?, G>, 052 C extends Comparable<? super C> 053> 054 extends Crossover<G, C> 055{ 056 057 private final double _p; 058 059 /** 060 * Creates a new linear-crossover with the given recombination 061 * probability and the line-scaling factor <em>p</em>. 062 * 063 * @param probability the recombination probability. 064 * @param p defines the possible location of the recombined chromosomes. If 065 * <em>p</em> = 0 then the children will be located along the line 066 * within the hypercube between the two points. If <em>p</em> > 0 067 * then the children may be located anywhere on the line, even 068 * somewhat outside the hypercube. 069 * @throws IllegalArgumentException if the {@code probability} is not in the 070 * valid range of {@code [0, 1]} or if {@code p} is smaller then zero 071 */ 072 public LineCrossover(final double probability, final double p) { 073 super(probability); 074 _p = Requires.nonNegative(p, "p"); 075 } 076 077 /** 078 * Creates a new linear-crossover with the given recombination 079 * probability. The parameter <em>p</em> is set to zero, which restricts the 080 * recombined chromosomes within the hypercube of the selected chromosomes 081 * (vectors). 082 * 083 * @param probability the recombination probability. 084 * @throws IllegalArgumentException if the {@code probability} is not in the 085 * valid range of {@code [0, 1]} 086 */ 087 public LineCrossover(final double probability) { 088 this(probability, 0); 089 } 090 091 /** 092 * Creates a new linear-crossover with default recombination 093 * probability ({@link #DEFAULT_ALTER_PROBABILITY}) and a <em>p</em> value 094 * of zero, which restricts the recombined chromosomes within the hypercube 095 * of the selected chromosomes (vectors). 096 */ 097 public LineCrossover() { 098 this(DEFAULT_ALTER_PROBABILITY, 0); 099 } 100 101 @Override 102 protected int crossover(final MSeq<G> v, final MSeq<G> w) { 103 final var random = RandomRegistry.random(); 104 105 final double min = v.get(0).min().doubleValue(); 106 final double max = v.get(0).max().doubleValue(); 107 108 final double a = random.nextDouble(-_p, 1 + _p); 109 final double b = random.nextDouble(-_p, 1 + _p); 110 111 boolean changed = false; 112 for (int i = 0, n = min(v.length(), w.length()); i < n; ++i) { 113 final double vi = v.get(i).doubleValue(); 114 final double wi = w.get(i).doubleValue(); 115 116 final double t = a*vi + (1 - a)*wi; 117 final double s = b*wi + (1 - b)*vi; 118 119 if (t >= min && s >= min && t < max && s < max) { 120 v.set(i, v.get(i).newInstance(t)); 121 w.set(i, w.get(i).newInstance(s)); 122 changed = true; 123 } 124 } 125 126 return changed ? 2 : 0; 127 } 128 129 @Override 130 public String toString() { 131 return format("%s[p=%f]", getClass().getSimpleName(), _probability); 132 } 133 134}