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