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.util.Objects.requireNonNull; 023import static io.jenetics.internal.util.SerialIO.readInt; 024import static io.jenetics.internal.util.SerialIO.writeInt; 025 026import java.io.DataInput; 027import java.io.DataOutput; 028import java.io.IOException; 029import java.io.InvalidObjectException; 030import java.io.ObjectInputStream; 031import java.io.Serial; 032import java.io.Serializable; 033import java.util.function.Function; 034import java.util.stream.DoubleStream; 035import java.util.stream.IntStream; 036import java.util.stream.Stream; 037 038import io.jenetics.util.DoubleRange; 039import io.jenetics.util.ISeq; 040import io.jenetics.util.IntRange; 041import io.jenetics.util.MSeq; 042 043/** 044 * Numeric chromosome implementation which holds 64-bit floating point numbers. 045 * 046 * @see DoubleGene 047 * 048 * @implNote 049 * This class is immutable and thread-safe. 050 * 051 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 052 * @since 1.6 053 * @version 6.1 054 */ 055public class DoubleChromosome 056 extends AbstractBoundedChromosome<Double, DoubleGene> 057 implements 058 NumericChromosome<Double, DoubleGene>, 059 Serializable 060{ 061 @Serial 062 private static final long serialVersionUID = 3L; 063 064 /** 065 * Create a new chromosome from the given {@code genes} and the allowed 066 * length range of the chromosome. 067 * 068 * @since 4.0 069 * 070 * @param genes the genes that form the chromosome. 071 * @param lengthRange the allowed length range of the chromosome 072 * @throws NullPointerException if one of the arguments is {@code null}. 073 * @throws IllegalArgumentException if the length of the gene sequence is 074 * empty, doesn't match with the allowed length range, the minimum 075 * or maximum of the range is smaller or equal zero, or the given 076 * range size is zero. 077 */ 078 protected DoubleChromosome( 079 final ISeq<DoubleGene> genes, 080 final IntRange lengthRange 081 ) { 082 super(genes, lengthRange); 083 } 084 085 @Override 086 public DoubleChromosome newInstance(final ISeq<DoubleGene> genes) { 087 return new DoubleChromosome(genes, lengthRange()); 088 } 089 090 @Override 091 public DoubleChromosome newInstance() { 092 return of(_min, _max, lengthRange()); 093 } 094 095 /** 096 * Maps the gene alleles of this chromosome, given as {@code double[]} array, 097 * by applying the given mapper function {@code f}. The mapped gene values 098 * are then wrapped into a newly created chromosome. 099 * {@snippet lang="java": 100 * final DoubleChromosome chromosome = null; // @replace substring='null' replacement="..." 101 * final DoubleChromosome normalized = chromosome.map(Main::normalize); 102 * 103 * static double[] normalize(final double[] values) { 104 * final double sum = sum(values); 105 * for (int i = 0; i < values.length; ++i) { 106 * values[i] /= sum; 107 * } 108 * return values; 109 * } 110 * } 111 * 112 * @since 6.1 113 * 114 * @param f the mapper function 115 * @return a newly created chromosome with the mapped gene values 116 * @throws NullPointerException if the mapper function is {@code null}. 117 * @throws IllegalArgumentException if the length of the mapped 118 * {@code double[]} array is empty or doesn't match with the allowed 119 * length range 120 */ 121 public DoubleChromosome map(final Function<? super double[], double[]> f) { 122 requireNonNull(f); 123 124 final var range = DoubleRange.of(_min, _max); 125 final var genes = DoubleStream.of(f.apply(toArray())) 126 .mapToObj(v -> DoubleGene.of(v, range)) 127 .collect(ISeq.toISeq()); 128 129 return newInstance(genes); 130 } 131 132 /** 133 * Returns a sequential stream of the alleles with this chromosome as its 134 * source. 135 * 136 * @since 4.3 137 * 138 * @return a sequential stream of alleles 139 */ 140 public DoubleStream doubleStream() { 141 return IntStream.range(0, length()).mapToDouble(this::doubleValue); 142 } 143 144 /** 145 * Returns a double array containing all the elements in this chromosome 146 * in a proper sequence. If the chromosome fits in the specified array, it is 147 * returned therein. Otherwise, a new array is allocated with the length of 148 * this chromosome. 149 * 150 * @since 3.0 151 * 152 * @param array the array into which the elements of these chromosomes are to 153 * be stored, if it is big enough; otherwise, a new array is 154 * allocated for this purpose. 155 * @return an array containing the elements of this chromosome 156 * @throws NullPointerException if the given {@code array} is {@code null} 157 */ 158 public double[] toArray(final double[] array) { 159 final double[] a = array.length >= length() 160 ? array 161 : new double[length()]; 162 163 for (int i = length(); --i >= 0;) { 164 a[i] = doubleValue(i); 165 } 166 167 return a; 168 } 169 170 /** 171 * Returns a double array containing all the elements in this chromosome 172 * in a proper sequence. 173 * 174 * @since 3.0 175 * 176 * @return an array containing the elements of this chromosome 177 */ 178 public double[] toArray() { 179 return toArray(new double[length()]); 180 } 181 182 183 /* ************************************************************************* 184 * Static factory methods. 185 * ************************************************************************/ 186 187 /** 188 * Create a new {@code DoubleChromosome} with the given genes. 189 * 190 * @param genes the genes of the chromosome. 191 * @return a new chromosome with the given genes. 192 * @throws IllegalArgumentException if the length of the genes array is 193 * empty or the given {@code genes} doesn't have the same range. 194 * @throws NullPointerException if the given {@code genes} array is 195 * {@code null} 196 */ 197 public static DoubleChromosome of(final DoubleGene... genes) { 198 checkGeneRange(Stream.of(genes).map(DoubleGene::range)); 199 return new DoubleChromosome(ISeq.of(genes), IntRange.of(genes.length)); 200 } 201 202 /** 203 * Create a new {@code DoubleChromosome} with the given genes. 204 * 205 * @since 4.3 206 * 207 * @param genes the genes of the chromosome. 208 * @return a new chromosome with the given genes. 209 * @throws NullPointerException if the given {@code genes} are {@code null} 210 * @throws IllegalArgumentException if the of the genes iterable is empty or 211 * the given {@code genes} doesn't have the same range. 212 */ 213 public static DoubleChromosome of(final Iterable<DoubleGene> genes) { 214 final ISeq<DoubleGene> values = ISeq.of(genes); 215 checkGeneRange(values.stream().map(DoubleGene::range)); 216 return new DoubleChromosome(values, IntRange.of(values.length())); 217 } 218 219 /** 220 * Create a new random chromosome. 221 * 222 * @since 4.0 223 * 224 * @param min the min value of the {@link DoubleGene}s (inclusively). 225 * @param max the max value of the {@link DoubleGene}s (exclusively). 226 * @param lengthRange the allowed length range of the chromosome. 227 * @return a new {@code DoubleChromosome} with the given parameter 228 * @throws IllegalArgumentException if the length of the gene sequence is 229 * empty, doesn't match with the allowed length range, the minimum 230 * or maximum of the range is smaller or equal zero, or the given 231 * range size is zero. 232 * @throws NullPointerException if the given {@code lengthRange} is 233 * {@code null} 234 */ 235 public static DoubleChromosome of( 236 final double min, 237 final double max, 238 final IntRange lengthRange 239 ) { 240 final ISeq<DoubleGene> genes = DoubleGene.seq(min, max, lengthRange); 241 return new DoubleChromosome(genes, lengthRange); 242 } 243 244 /** 245 * Create a new random {@code DoubleChromosome}. 246 * 247 * @param min the min value of the {@link DoubleGene}s (inclusively). 248 * @param max the max value of the {@link DoubleGene}s (exclusively). 249 * @param length the length of the chromosome. 250 * @return a new {@code DoubleChromosome} with the given parameter 251 * @throws IllegalArgumentException if the {@code length} is smaller than 252 * one. 253 */ 254 public static DoubleChromosome of( 255 final double min, 256 final double max, 257 final int length 258 ) { 259 return of(min, max, IntRange.of(length)); 260 } 261 262 /** 263 * Create a new random chromosome. 264 * 265 * @since 4.0 266 * 267 * @param range the integer range of the chromosome. 268 * @param lengthRange the allowed length range of the chromosome. 269 * @return a new {@code DoubleChromosome} with the given parameter 270 * @throws IllegalArgumentException if the length of the gene sequence is 271 * empty, doesn't match with the allowed length range, the minimum 272 * or maximum of the range is smaller or equal zero, or the given 273 * range size is zero. 274 * @throws NullPointerException if the given {@code lengthRange} is 275 * {@code null} 276 */ 277 public static DoubleChromosome of( 278 final DoubleRange range, 279 final IntRange lengthRange 280 ) { 281 return of(range.min(), range.max(), lengthRange); 282 } 283 284 /** 285 * Create a new random {@code DoubleChromosome}. 286 * 287 * @since 3.2 288 * 289 * @param range the integer range of the chromosome. 290 * @param length the length of the chromosome. 291 * @return a new random {@code DoubleChromosome} 292 * @throws NullPointerException if the given {@code range} is {@code null} 293 * @throws IllegalArgumentException if the {@code length} is smaller than 294 * one. 295 */ 296 public static DoubleChromosome of(final DoubleRange range, final int length) { 297 return of(range.min(), range.max(), length); 298 } 299 300 /** 301 * Create a new random {@code DoubleChromosome} of length one. 302 * 303 * @param min the minimal value of this chromosome (inclusively). 304 * @param max the maximal value of this chromosome (exclusively). 305 * @return a new {@code DoubleChromosome} with the given parameter 306 */ 307 public static DoubleChromosome of(final double min, final double max) { 308 return of(min, max, 1); 309 } 310 311 /** 312 * Create a new random {@code DoubleChromosome} of length one. 313 * 314 * @since 3.2 315 * 316 * @param range the double range of the chromosome. 317 * @return a new random {@code DoubleChromosome} of length one 318 * @throws NullPointerException if the given {@code range} is {@code null} 319 */ 320 public static DoubleChromosome of(final DoubleRange range) { 321 return of(range.min(), range.max()); 322 } 323 324 325 /* ************************************************************************* 326 * Java object serialization 327 * ************************************************************************/ 328 329 @Serial 330 private Object writeReplace() { 331 return new SerialProxy(SerialProxy.DOUBLE_CHROMOSOME, this); 332 } 333 334 @Serial 335 private void readObject(final ObjectInputStream stream) 336 throws InvalidObjectException 337 { 338 throw new InvalidObjectException("Serialization proxy required."); 339 } 340 341 void write(final DataOutput out) throws IOException { 342 writeInt(length(), out); 343 writeInt(lengthRange().min(), out); 344 writeInt(lengthRange().max(), out); 345 out.writeDouble(_min); 346 out.writeDouble(_max); 347 348 for (int i = 0, n = length(); i < n; ++i) { 349 out.writeDouble(doubleValue(i)); 350 } 351 } 352 353 static DoubleChromosome read(final DataInput in) throws IOException { 354 final var length = readInt(in); 355 final var lengthRange = IntRange.of(readInt(in), readInt(in)); 356 final var min = in.readDouble(); 357 final var max = in.readDouble(); 358 359 final MSeq<DoubleGene> values = MSeq.ofLength(length); 360 for (int i = 0; i < length; ++i) { 361 values.set(i, DoubleGene.of(in.readDouble(), min, max)); 362 } 363 364 return new DoubleChromosome(values.toISeq(), lengthRange); 365 } 366 367}