001/* 002 * Java Genetic Algorithm Library (jenetics-8.3.0). 003 * Copyright (c) 2007-2025 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 java.util.random.RandomGenerator; 023 024import io.jenetics.stat.Sampler; 025import io.jenetics.stat.Samplers; 026import io.jenetics.util.DoubleRange; 027 028/** 029 * The GaussianMutator class performs the mutation of a {@link NumericGene}. 030 * This mutator picks a new value based on a Gaussian distribution, defined by 031 * the {@link Shape} parameter of the mutator. 032 * <br> 033 * <img src="doc-files/gaussian-mutator-sigma.svg" alt="Sigma graph" width="500"/> 034 * <br> 035 * 036 * @param <G> the gene type 037 * @param <C> the allele type 038 * 039 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 040 * @since 1.0 041 * @version 8.3 042 */ 043public class GaussianMutator< 044 G extends NumericGene<?, G>, 045 C extends Comparable<? super C> 046> 047 extends SamplerMutator<G, C> 048{ 049 050 /** 051 * The parameters which define the <em>shape</em> of gaussian distribution 052 * of new gene values. 053 * <p> 054 * <b>shift</b><br> 055 * The {@code shift} value shifts the mean value of the distribution. Positive 056 * values shifts it right and negative values left. Its value 057 * must be within the range of {@code [-1, 1]}. This restriction ensures the 058 * numerical stability of the mutator. The actual µ value is calculated as 059 * follows: {@code µ = (shift + 1)*((max - min)/2)}. 060 * <br> 061 * <img src="doc-files/gaussian-mutator-shift.svg" alt="Shift graph" width="500"/> 062 * <br> 063 * <b>sigma</b><br> 064 * The {@code sigma} value <em>stretches</em> and <em>compresses</em> 065 * the distribution and must be within the range of {@code [0.1, 5]}. This 066 * restriction ensures the numerical stability of the mutator. The actual σ 067 * value is calculated as follows: {@code σ = ((max - min)/2)/sigma}. 068 * <br> 069 * <img src="doc-files/gaussian-mutator-sigma.svg" alt="Sigma graph" width="500"/> 070 * <br> 071 * 072 * @param shift the shift parameter, S, determining the mean value of the 073 * created mutation value distribution 074 * @param sigma the sigma parameter, Σ, determining the standard deviation of 075 * the created mutation value distribution 076 * @since 8.3 077 * @version 8.3 078 */ 079 public record Shape(double shift, double sigma) implements Sampler { 080 081 /** 082 * Create a new mutation distribution shape. 083 * 084 * @param shift the shift parameter, S, determining the mean value of the 085 * crated mutation value distribution 086 * @param sigma the sigma parameter, Σ, determining the standard deviation of 087 * the created mutation value distribution 088 * @throws IllegalArgumentException if {@code shift < -1 || shift > 1} of 089 * {@code sigma < 0.1 || sigma > 5} 090 */ 091 public Shape { 092 if (shift < -1 || shift > 1) { 093 throw new IllegalArgumentException( 094 "Shift must be within the range [-1, 1]: " + shift 095 ); 096 } 097 if (sigma < 0.1 || sigma > 5) { 098 throw new IllegalArgumentException( 099 "Sigma must be within the range [0.1, 5]: " + sigma 100 ); 101 } 102 } 103 104 @Override 105 public double sample(final RandomGenerator random, final DoubleRange range) { 106 final var sig = (range.max() - range.min())/2.0; 107 final var mean = sig + sig*shift; 108 final var stddev = sig/sigma; 109 110 return Samplers.gaussian(mean, stddev).sample(random, range); 111 } 112 113 double stddev(final DoubleRange range) { 114 return ((range.max() - range.min())/2.0)/sigma; 115 } 116 117 double mean(final DoubleRange range) { 118 return (shift + 1)*((range.max() - range.min())/2.0); 119 } 120 121 } 122 123 /** 124 * The default shape of the mutator: {@code Shape[shift=0, sigma=1]}. 125 */ 126 public static final Shape DEFAULT_SHAPE = new Shape(0, 1); 127 128 /** 129 * Create a new Gaussian mutator with the given parameter. 130 * 131 * @param probability the mutation probabilities 132 * @param shape the <em>shape</em> of the mutation value distribution 133 */ 134 public GaussianMutator(final double probability, final Shape shape) { 135 super(probability, shape); 136 } 137 138 /** 139 * Create a new Gaussian mutator with the given mutation value distribution 140 * shape and the default mutation probability, {@link #DEFAULT_ALTER_PROBABILITY}. 141 * 142 * @param shape the mutation value distribution shape 143 */ 144 public GaussianMutator(final Shape shape) { 145 this(DEFAULT_ALTER_PROBABILITY, shape); 146 } 147 148 /** 149 * Crate a new Gaussian mutator with the given mutation probability and the 150 * default shape, {@link #DEFAULT_SHAPE}. 151 * 152 * @param probability the mutation probability 153 */ 154 public GaussianMutator(final double probability) { 155 this(probability, DEFAULT_SHAPE); 156 } 157 158 /** 159 * Create a new Gaussian mutator with the default mutation probability, 160 * {@link #DEFAULT_ALTER_PROBABILITY}, and default distribution shape, 161 * {@link #DEFAULT_SHAPE}. 162 */ 163 public GaussianMutator() { 164 this(DEFAULT_ALTER_PROBABILITY, DEFAULT_SHAPE); 165 } 166 167}