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