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