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.ext; 021 022import static java.lang.Math.abs; 023import static java.lang.Math.clamp; 024import static java.lang.Math.pow; 025import static java.lang.String.format; 026 027import io.jenetics.Crossover; 028import io.jenetics.NumericGene; 029import io.jenetics.internal.math.Randoms; 030import io.jenetics.internal.util.Requires; 031import io.jenetics.util.MSeq; 032import io.jenetics.util.RandomRegistry; 033 034/** 035 * Performs the simulated binary crossover (SBX) on a {@code Chromosome} of 036 * {@link NumericGene}s such that each position is either crossed contracted or 037 * expanded with a certain probability. The probability distribution is designed 038 * such that the children will lie closer to their parents, as is the case with 039 * the single-point binary crossover. 040 * <p> 041 * It is implemented as described in Deb, K. and Agrawal, R. B. 1995. Simulated 042 * binary crossover for continuous search space. Complex Systems, 9, pp. 115-148. 043 * 044 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 045 * @since 3.5 046 * @version 6.0 047 */ 048public class SimulatedBinaryCrossover< 049 G extends NumericGene<?, G>, 050 C extends Comparable<? super C> 051> 052 extends Crossover<G, C> 053{ 054 private final double _contiguity; 055 056 /** 057 * Create a new <i>simulated binary crossover</i> alterer with the given 058 * parameters. 059 * 060 * @param probability the recombination probability 061 * @param contiguity the contiguity value that specifies how close a child 062 * should be to its parents (larger value means closer). The value 063 * must be greater or equal than 0. Typical values are in the range 064 * [2..5]. 065 * @throws IllegalArgumentException if the {@code probability} is not in the 066 * valid range of {@code [0, 1]} 067 * @throws IllegalArgumentException if {@code contiguity} is smaller than 068 * zero 069 */ 070 public SimulatedBinaryCrossover( 071 final double probability, 072 final double contiguity 073 ) { 074 super(probability); 075 _contiguity = Requires.nonNegative(contiguity); 076 } 077 078 /** 079 * Create a new <i>simulated binary crossover</i> alterer with the given 080 * parameters. The <i>contiguity</i> value is set to {@code 2.5}. 081 * 082 * @param probability the recombination probability 083 * @throws IllegalArgumentException if the {@code probability} is not in the 084 * valid range of {@code [0, 1]} 085 * @throws IllegalArgumentException if {@code contiguity} is smaller than 086 * zero 087 */ 088 public SimulatedBinaryCrossover(final double probability) { 089 this(probability, 2.5); 090 } 091 092 /** 093 * Return the <i>contiguity</i> value of the crossover. 094 * 095 * @return the <i>contiguity</i> value of the crossover 096 */ 097 public double contiguity() { 098 return _contiguity; 099 } 100 101 @Override 102 protected int crossover(final MSeq<G> that, final MSeq<G> other) { 103 return (int) Randoms.indexes(RandomRegistry.random(), that.length(), 0.5) 104 .peek(i -> crossover(that, other, i)) 105 .count(); 106 } 107 108 private void crossover(final MSeq<G> that, final MSeq<G> other, final int i) { 109 final var random = RandomRegistry.random(); 110 111 final double u = random.nextDouble(); 112 final double beta; 113 if (u < 0.5) { 114 // If u is smaller than 0.5 perform a contracting crossover. 115 beta = pow(2*u, 1.0/(_contiguity + 1)); 116 } else if (u > 0.5) { 117 // Otherwise, perform an expanding crossover. 118 beta = pow(0.5/(1.0 - u), 1.0/(_contiguity + 1)); 119 } else { 120 beta = 1; 121 } 122 123 final double v1 = that.get(i).doubleValue(); 124 final double v2 = other.get(i).doubleValue(); 125 final double v = random.nextBoolean() 126 ? ((v1 - v2)*0.5) - beta*0.5*abs(v1 - v2) 127 : ((v1 - v2)*0.5) + beta*0.5*abs(v1 - v2); 128 129 final double min = that.get(i).min().doubleValue(); 130 final double max = that.get(i).max().doubleValue(); 131 that.set(i, that.get(i).newInstance(clamp(v, min, max))); 132 } 133 134 @Override 135 public String toString() { 136 return format( 137 "SimulatedBinaryCrossover[p=%f, c=%f]", 138 _probability, _contiguity 139 ); 140 } 141 142}