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.engine; 021 022import static java.lang.String.format; 023import static java.util.Map.entry; 024import static java.util.Objects.requireNonNull; 025import static java.util.function.Function.identity; 026 027import java.util.Arrays; 028import java.util.HashMap; 029import java.util.Map; 030import java.util.Map.Entry; 031import java.util.Objects; 032import java.util.function.Predicate; 033import java.util.function.Supplier; 034import java.util.stream.Collectors; 035import java.util.stream.DoubleStream; 036import java.util.stream.IntStream; 037import java.util.stream.LongStream; 038import java.util.stream.Stream; 039 040import io.jenetics.AnyChromosome; 041import io.jenetics.AnyGene; 042import io.jenetics.BitChromosome; 043import io.jenetics.BitGene; 044import io.jenetics.DoubleChromosome; 045import io.jenetics.DoubleGene; 046import io.jenetics.EnumGene; 047import io.jenetics.Gene; 048import io.jenetics.Genotype; 049import io.jenetics.IntegerChromosome; 050import io.jenetics.IntegerGene; 051import io.jenetics.LongChromosome; 052import io.jenetics.LongGene; 053import io.jenetics.PermutationChromosome; 054import io.jenetics.internal.util.Bits; 055import io.jenetics.internal.util.Predicates; 056import io.jenetics.internal.util.Requires; 057import io.jenetics.util.DoubleRange; 058import io.jenetics.util.ISeq; 059import io.jenetics.util.IntRange; 060import io.jenetics.util.LongRange; 061 062/** 063 * This class contains factory methods for creating common problem encodings. 064 * 065 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 066 * @since 3.2 067 * @version 5.2 068 */ 069public final class Codecs { 070 071 private Codecs() {} 072 073 /** 074 * Return a scalar {@link InvertibleCodec} for the given range. 075 * 076 * @param domain the domain of the returned {@code Codec} 077 * @return a new scalar {@code Codec} with the given domain. 078 * @throws NullPointerException if the given {@code domain} is {@code null} 079 */ 080 public static InvertibleCodec<Integer, IntegerGene> 081 ofScalar(final IntRange domain) { 082 requireNonNull(domain); 083 084 return InvertibleCodec.of( 085 Genotype.of(IntegerChromosome.of(domain)), 086 gt -> gt.chromosome().gene().allele(), 087 val -> Genotype.of(IntegerChromosome.of(IntegerGene.of(val, domain))) 088 ); 089 } 090 091 /** 092 * Return a scalar {@link InvertibleCodec} for the given range. 093 * 094 * @param domain the domain of the returned {@code Codec} 095 * @return a new scalar {@code Codec} with the given domain. 096 * @throws NullPointerException if the given {@code domain} is {@code null} 097 */ 098 public static InvertibleCodec<Long, LongGene> 099 ofScalar(final LongRange domain) { 100 requireNonNull(domain); 101 102 return InvertibleCodec.of( 103 Genotype.of(LongChromosome.of(domain)), 104 gt -> gt.gene().allele(), 105 val -> Genotype.of(LongChromosome.of(LongGene.of(val, domain))) 106 ); 107 } 108 109 /** 110 * Return a scalar {@link InvertibleCodec} for the given range. 111 * 112 * @param domain the domain of the returned {@code Codec} 113 * @return a new scalar {@code Codec} with the given domain. 114 * @throws NullPointerException if the given {@code domain} is {@code null} 115 */ 116 public static InvertibleCodec<Double, DoubleGene> 117 ofScalar(final DoubleRange domain) { 118 requireNonNull(domain); 119 120 return InvertibleCodec.of( 121 Genotype.of(DoubleChromosome.of(domain)), 122 gt -> gt.gene().allele(), 123 val -> Genotype.of(DoubleChromosome.of(DoubleGene.of(val, domain))) 124 ); 125 } 126 127 /** 128 * Return a scala {@link Codec} with the given allele {@link Supplier} and 129 * allele {@code validator}. The {@code supplier} is responsible for 130 * creating new random alleles, and the {@code validator} can verify it. 131 * <p> 132 * The following example shows a codec which creates and verifies 133 * {@code BigInteger} objects. 134 * {@snippet lang="java": 135 * final Codec<BigInteger, AnyGene<BigInteger>> codec = Codecs.of( 136 * // Create new random 'BigInteger' object. 137 * () -> { 138 * final byte[] data = new byte[100]; 139 * RandomRegistry.getRandom().nextBytes(data); 140 * return new BigInteger(data); 141 * }, 142 * // Verify that bit 7 is set. (For illustration purpose.) 143 * bi -> bi.testBit(7) 144 * ); 145 * } 146 * 147 * @see AnyGene#of(Supplier, Predicate) 148 * @see AnyChromosome#of(Supplier, Predicate) 149 * 150 * @param <A> the allele type 151 * @param supplier the allele-supplier which is used for creating new, 152 * random alleles 153 * @param validator the validator used for validating the created gene. This 154 * predicate is used in the {@link AnyGene#isValid()} method. 155 * @return a new {@code Codec} with the given parameters 156 * @throws NullPointerException if one of the parameters is {@code null} 157 */ 158 public static <A> Codec<A, AnyGene<A>> ofScalar( 159 final Supplier<? extends A> supplier, 160 final Predicate<? super A> validator 161 ) { 162 return Codec.of( 163 Genotype.of(AnyChromosome.of(supplier, validator)), 164 gt -> gt.gene().allele() 165 ); 166 } 167 168 /** 169 * Return a scala {@link Codec} with the given allele {@link Supplier} and 170 * allele {@code validator}. The {@code supplier} is responsible for 171 * creating new random alleles. 172 * 173 * @see #ofScalar(Supplier, Predicate) 174 * @see AnyGene#of(Supplier) 175 * @see AnyChromosome#of(Supplier) 176 * 177 * @param <A> the allele type 178 * @param supplier the allele-supplier which is used for creating new, 179 * random alleles 180 * @return a new {@code Codec} with the given parameters 181 * @throws NullPointerException if the parameter is {@code null} 182 */ 183 public static <A> Codec<A, AnyGene<A>> ofScalar( 184 final Supplier<? extends A> supplier 185 ) { 186 return Codec.of( 187 Genotype.of(AnyChromosome.of(supplier)), 188 gt -> gt.gene().allele() 189 ); 190 } 191 192 /** 193 * Return a vector {@link InvertibleCodec} for the given range. All vector 194 * values are restricted by the same domain. 195 * 196 * @param domain the domain of the vector values 197 * @param length the vector length 198 * @return a new vector {@code Codec} 199 * @throws NullPointerException if the given {@code domain} is {@code null} 200 * @throws IllegalArgumentException if the {@code length} is smaller than 201 * one. 202 */ 203 public static InvertibleCodec<int[], IntegerGene> ofVector( 204 final IntRange domain, 205 final int length 206 ) { 207 return ofVector(domain, new IntRange(length)); 208 } 209 210 /** 211 * Return a vector {@link InvertibleCodec} for the given range. All vector 212 * values are restricted by the same domain. 213 * 214 * @since 7.0 215 * 216 * @param domain the domain of the vector values 217 * @param length the vector length range 218 * @return a new vector {@code Codec} 219 * @throws NullPointerException if the given {@code domain} is {@code null} 220 * @throws IllegalArgumentException if the {@code length} is smaller than 221 * one. 222 */ 223 public static InvertibleCodec<int[], IntegerGene> ofVector( 224 final IntRange domain, 225 final IntRange length 226 ) { 227 requireNonNull(domain); 228 Requires.positive(length.min()); 229 230 return InvertibleCodec.of( 231 Genotype.of(IntegerChromosome.of(domain, length)), 232 gt -> gt.chromosome().as(IntegerChromosome.class).toArray(), 233 val -> Genotype.of( 234 IntegerChromosome.of( 235 IntStream.of(val) 236 .mapToObj(i -> IntegerGene.of(i, domain)) 237 .collect(ISeq.toISeq()) 238 ) 239 ) 240 ); 241 } 242 243 /** 244 * Return a vector {@link InvertibleCodec} for the given range. All vector 245 * values are restricted by the same domain. 246 * 247 * @param domain the domain of the vector values 248 * @param length the vector length 249 * @return a new vector {@code Codec} 250 * @throws NullPointerException if the given {@code domain} is {@code null} 251 * @throws IllegalArgumentException if the {@code length} is smaller than 252 * one. 253 */ 254 public static InvertibleCodec<long[], LongGene> ofVector( 255 final LongRange domain, 256 final int length 257 ) { 258 return ofVector(domain, new IntRange(length)); 259 } 260 261 /** 262 * Return a vector {@link InvertibleCodec} for the given range. All vector 263 * values are restricted by the same domain. 264 * 265 * @since 7.0 266 * 267 * @param domain the domain of the vector values 268 * @param length the vector length range 269 * @return a new vector {@code Codec} 270 * @throws NullPointerException if the given {@code domain} is {@code null} 271 * @throws IllegalArgumentException if the {@code length} is smaller than 272 * one. 273 */ 274 public static InvertibleCodec<long[], LongGene> ofVector( 275 final LongRange domain, 276 final IntRange length 277 ) { 278 requireNonNull(domain); 279 Requires.positive(length.min()); 280 281 return InvertibleCodec.of( 282 Genotype.of(LongChromosome.of(domain, length)), 283 gt -> gt.chromosome().as(LongChromosome.class).toArray(), 284 val -> Genotype.of( 285 LongChromosome.of( 286 LongStream.of(val) 287 .mapToObj(l -> LongGene.of(l, domain)) 288 .collect(ISeq.toISeq()) 289 ) 290 ) 291 ); 292 } 293 294 /** 295 * Return a vector {@link InvertibleCodec} for the given range. All vector 296 * values are restricted by the same domain. Use the following code if you 297 * want to create {@code int[]} arrays and still using {@link DoubleGene}. 298 * The {@code int[]} array elements are created by casting the {@code double} 299 * values to {@code int} values. 300 * {@snippet lang=java: 301 * final Codec<int[], DoubleGene> codec = Codecs 302 * .ofVector(new DoubleRange(0, 100), 100) 303 * .map(ArrayConversions::doubleToIntArray); 304 * } 305 * If you want round the double values, you can use the following code. 306 * {@snippet lang=java: 307 * final Codec<int[], DoubleGene> codec = Codecs 308 * .ofVector(new DoubleRange(0, 100), 100) 309 * .map(ArrayConversions.doubleToIntArray(v -> (int)Math.round(v))); 310 * } 311 * 312 * @param domain the domain of the vector values 313 * @param length the vector length 314 * @return a new vector {@code Codec} 315 * @throws NullPointerException if the given {@code domain} is {@code null} 316 * @throws IllegalArgumentException if the {@code length} is smaller than 317 * one. 318 */ 319 public static InvertibleCodec<double[], DoubleGene> ofVector( 320 final DoubleRange domain, 321 final int length 322 ) { 323 return ofVector(domain, new IntRange(length)); 324 } 325 326 /** 327 * Return a vector {@link InvertibleCodec} for the given range. All vector 328 * values are restricted by the same domain. 329 * 330 * @since 7.0 331 * 332 * @param domain the domain of the vector values 333 * @param length the vector length range 334 * @return a new vector {@code Codec} 335 * @throws NullPointerException if the given {@code domain} is {@code null} 336 * @throws IllegalArgumentException if the {@code length} is smaller than 337 * one. 338 */ 339 public static InvertibleCodec<double[], DoubleGene> ofVector( 340 final DoubleRange domain, 341 final IntRange length 342 ) { 343 requireNonNull(domain); 344 Requires.positive(length.min()); 345 346 return InvertibleCodec.of( 347 Genotype.of(DoubleChromosome.of(domain, length)), 348 gt -> gt.chromosome().as(DoubleChromosome.class).toArray(), 349 val -> Genotype.of( 350 DoubleChromosome.of( 351 DoubleStream.of(val) 352 .mapToObj(d -> DoubleGene.of(d, domain)) 353 .collect(ISeq.toISeq()) 354 ) 355 ) 356 ); 357 } 358 359 /** 360 * Create a vector {@link InvertibleCodec} for the given ranges. Each vector 361 * element might have a different domain. The vector length is equal to the 362 * number of domains. 363 * 364 * @param domains the domain ranges 365 * @return a new vector {@code Codec} 366 * @throws NullPointerException if one of the arguments is {@code null} 367 * @throws IllegalArgumentException if the {@code domains} array is empty 368 */ 369 public static InvertibleCodec<int[], IntegerGene> 370 ofVector(final IntRange... domains) { 371 if (domains.length == 0) { 372 throw new IllegalArgumentException("Domains must not be empty."); 373 } 374 375 final ISeq<IntegerChromosome> chromosomes = Stream.of(domains) 376 .peek(Objects::requireNonNull) 377 .map(IntegerGene::of) 378 .map(IntegerChromosome::of) 379 .collect(ISeq.toISeq()); 380 381 return InvertibleCodec.of( 382 Genotype.of(chromosomes), 383 gt -> { 384 final int[] args = new int[gt.length()]; 385 for (int i = gt.length(); --i >= 0;) { 386 args[i] = gt.get(i).gene().intValue(); 387 } 388 return args; 389 }, 390 val -> Genotype.of( 391 IntStream.range(0, val.length) 392 .mapToObj(i -> 393 IntegerChromosome.of(IntegerGene.of(val[i], domains[i]))) 394 .collect(ISeq.toISeq()) 395 ) 396 ); 397 } 398 399 /** 400 * Create a vector {@link InvertibleCodec} for the given ranges. Each vector 401 * element might have a different domain. The vector length is equal to the 402 * number of domains. 403 * 404 * @param domains the domain ranges 405 * @return a new vector {@code Codec} 406 * @throws NullPointerException if one of the arguments is {@code null} 407 * @throws IllegalArgumentException if the {@code domains} array is empty 408 */ 409 public static InvertibleCodec<long[], LongGene> 410 ofVector(final LongRange... domains) { 411 if (domains.length == 0) { 412 throw new IllegalArgumentException("Domains must not be empty."); 413 } 414 415 final ISeq<LongChromosome> chromosomes = Stream.of(domains) 416 .peek(Objects::requireNonNull) 417 .map(LongGene::of) 418 .map(LongChromosome::of) 419 .collect(ISeq.toISeq()); 420 421 return InvertibleCodec.of( 422 Genotype.of(chromosomes), 423 gt -> { 424 final long[] args = new long[gt.length()]; 425 for (int i = gt.length(); --i >= 0;) { 426 args[i] = gt.get(i).gene().longValue(); 427 } 428 return args; 429 }, 430 val -> Genotype.of( 431 IntStream.range(0, val.length) 432 .mapToObj(i -> 433 LongChromosome.of(LongGene.of(val[i], domains[i]))) 434 .collect(ISeq.toISeq()) 435 ) 436 ); 437 } 438 439 /** 440 * Create a vector {@link InvertibleCodec} for the given ranges. Each vector 441 * element might have a different domain. The vector length is equal to the 442 * number of domains. 443 * 444 * @param domains the domain ranges 445 * @return a new vector {@code Codec} 446 * @throws NullPointerException if one of the arguments is {@code null} 447 * @throws IllegalArgumentException if the {@code domains} array is empty 448 */ 449 public static InvertibleCodec<double[], DoubleGene> 450 ofVector(final DoubleRange... domains) { 451 if (domains.length == 0) { 452 throw new IllegalArgumentException("Domains must not be empty."); 453 } 454 455 final ISeq<DoubleChromosome> chromosomes = Stream.of(domains) 456 .peek(Objects::requireNonNull) 457 .map(DoubleGene::of) 458 .map(DoubleChromosome::of) 459 .collect(ISeq.toISeq()); 460 461 return InvertibleCodec.of( 462 Genotype.of(chromosomes), 463 gt -> { 464 final double[] args = new double[gt.length()]; 465 for (int i = gt.length(); --i >= 0;) { 466 args[i] = gt.get(i).gene().doubleValue(); 467 } 468 return args; 469 }, 470 val -> Genotype.of( 471 IntStream.range(0, val.length) 472 .mapToObj(i -> 473 DoubleChromosome.of(DoubleGene.of(val[i], domains[i]))) 474 .collect(ISeq.toISeq()) 475 ) 476 ); 477 } 478 479 /** 480 * Return a scala {@link Codec} with the given allele {@link Supplier}, 481 * allele {@code validator} and {@code Chromosome} length. The 482 * {@code supplier} is responsible for creating new random alleles, and the 483 * {@code validator} can verify it. 484 * <p> 485 * The following example shows a codec which creates and verifies 486 * {@code BigInteger} object arrays. 487 * {@snippet lang="java": 488 * final Codec<BigInteger[], AnyGene<BigInteger>> codec = Codecs.of( 489 * // Create new random 'BigInteger' object. 490 * () -> { 491 * final byte[] data = new byte[100]; 492 * RandomRegistry.getRandom().nextBytes(data); 493 * return new BigInteger(data); 494 * }, 495 * // Verify that bit 7 is set. (For illustration purpose.) 496 * bi -> bi.testBit(7), 497 * // The 'Chromosome' length. 498 * 123 499 * ); 500 * } 501 * 502 * @see AnyChromosome#of(Supplier, Predicate, Predicate, int) 503 * 504 * @param <A> the allele type 505 * @param supplier the allele-supplier which is used for creating new, 506 * random alleles 507 * @param alleleValidator the validator used for validating the created gene. 508 * This predicate is used in the {@link AnyGene#isValid()} method. 509 * @param alleleSeqValidator the validator used for validating the created 510 * chromosome. This predicate is used in the 511 * {@link AnyChromosome#isValid()} method. 512 * @param length the vector length 513 * @return a new {@code Codec} with the given parameters 514 * @throws NullPointerException if one of the parameters is {@code null} 515 * @throws IllegalArgumentException if the length of the vector is smaller 516 * than one. 517 */ 518 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector( 519 final Supplier<? extends A> supplier, 520 final Predicate<? super A> alleleValidator, 521 final Predicate<? super ISeq<A>> alleleSeqValidator, 522 final int length 523 ) { 524 requireNonNull(supplier); 525 requireNonNull(alleleSeqValidator); 526 requireNonNull(alleleSeqValidator); 527 Requires.positive(length); 528 529 return Codec.of( 530 Genotype.of(AnyChromosome 531 .of(supplier, alleleValidator, alleleSeqValidator, length)), 532 gt -> gt.chromosome().stream() 533 .map(Gene::allele) 534 .collect(ISeq.toISeq()) 535 ); 536 } 537 538 /** 539 * Return a scala {@link Codec} with the given allele {@link Supplier}, 540 * allele {@code validator} and {@code Chromosome} length. The 541 * {@code supplier} is responsible for creating new random alleles, and the 542 * {@code validator} can verify it. 543 * 544 * @param <A> the allele type 545 * @param supplier the allele-supplier which is used for creating new, 546 * random alleles 547 * @param validator the validator used for validating the created gene. This 548 * predicate is used in the {@link AnyGene#isValid()} method. 549 * @param length the vector length 550 * @return a new {@code Codec} with the given parameters 551 * @throws NullPointerException if one of the parameters is {@code null} 552 * @throws IllegalArgumentException if the length of the vector is smaller 553 * than one. 554 */ 555 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector( 556 final Supplier<? extends A> supplier, 557 final Predicate<? super A> validator, 558 final int length 559 ) { 560 return ofVector( 561 supplier, 562 validator, 563 Predicates.True(), 564 length 565 ); 566 } 567 568 /** 569 * Return a scala {@link Codec} with the given allele {@link Supplier} and 570 * {@code Chromosome} length. The {@code supplier} is responsible for 571 * creating new random alleles. 572 * 573 * @param <A> the allele type 574 * @param supplier the allele-supplier which is used for creating new, 575 * random alleles 576 * @param length the vector length 577 * @return a new {@code Codec} with the given parameters 578 * @throws NullPointerException if one of the parameters is {@code null} 579 * @throws IllegalArgumentException if the length of the vector is smaller 580 * than one. 581 */ 582 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector( 583 final Supplier<? extends A> supplier, 584 final int length 585 ) { 586 return ofVector(supplier, Predicates.TRUE, length); 587 } 588 589 /** 590 * Create a vector ({@link ISeq}) of domain objects, which are created with 591 * the given {@code codec}. 592 * {@snippet lang=java: 593 * // Domain model of solution space. 594 * record Path(WayPoint[] stops) {} 595 * 596 * // Codec fora single GPS point (latitude, longitude). 597 * final Codec<WayPoint, DoubleGene> wpc = Codec.combine( 598 * Codecs.ofScalar(new DoubleRange(30, 50)), // latitude 599 * Codecs.ofScalar(new DoubleRange(69, 72)), // longitude 600 * WayPoint::of 601 * ); 602 * 603 * // Codec for the path object. 604 * final Codec<Path, DoubleGene> pc = Codecs.ofVector(wpc, 10) 605 * .map(points -> points.toArray(WayPoint[]::new)) 606 * .map(Path::new); 607 * 608 * final Path path = pc.decode(pc.encoding().newInstance()); 609 * } 610 * 611 * @since 8.1 612 * 613 * @param codec the codec for the <em>domain</em> object 614 * @param length the length of the vector. 615 * @return a codec for a sequence of domain objects 616 * @param <S> the type of the domain object 617 * @param <G> the encoding gene type 618 * @throws NullPointerException if the given {@code codec} is {@code null} 619 * @throws IllegalArgumentException if the length is smaller than 1 620 */ 621 @SuppressWarnings("unchecked") 622 public static <S, G extends Gene<?, G>> Codec<ISeq<S>, G> 623 ofVector(final Codec<? extends S, G> codec, final int length) { 624 requireNonNull(codec); 625 if (length <= 0) { 626 throw new IllegalArgumentException("Length must be positive: " + length); 627 } 628 629 return Codec.combine( 630 IntStream.range(0, length) 631 .mapToObj(__ -> codec) 632 .collect(ISeq.toISeq()), 633 objects -> ISeq.of((S[])objects) 634 ); 635 } 636 637 /** 638 * Create a permutation {@link InvertibleCodec} of integer in the range 639 * {@code [0, length)}. 640 * 641 * @param length the number of permutation elements 642 * @return a permutation {@code Codec} of integers 643 * @throws IllegalArgumentException if the {@code length} is smaller than 644 * one. 645 */ 646 public static InvertibleCodec<int[], EnumGene<Integer>> 647 ofPermutation(final int length) { 648 Requires.positive(length); 649 650 final PermutationChromosome<Integer> chromosome = 651 PermutationChromosome.ofInteger(length); 652 653 final Map<Integer, EnumGene<Integer>> genes = chromosome.stream() 654 .collect(Collectors.toMap(EnumGene::allele, identity())); 655 656 return InvertibleCodec.of( 657 Genotype.of(chromosome), 658 gt -> gt.chromosome().stream() 659 .mapToInt(EnumGene::allele) 660 .toArray(), 661 val -> Genotype.of( 662 new PermutationChromosome<>( 663 IntStream.of(val) 664 .mapToObj(genes::get) 665 .collect(ISeq.toISeq()) 666 ) 667 ) 668 ); 669 } 670 671 /** 672 * Create a permutation {@link InvertibleCodec} with the given alleles. 673 * 674 * @param alleles the alleles of the permutation 675 * @param <T> the allele type 676 * @return a new permutation {@code Codec} 677 * @throws IllegalArgumentException if the given allele array is empty 678 * @throws NullPointerException if one of the alleles is {@code null} 679 */ 680 public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> 681 ofPermutation(final ISeq<? extends T> alleles) { 682 if (alleles.isEmpty()) { 683 throw new IllegalArgumentException( 684 "Empty allele array is not allowed." 685 ); 686 } 687 688 final Map<T, EnumGene<T>> genes = 689 IntStream.range(0, alleles.length()) 690 .mapToObj(i -> EnumGene.<T>of(i, alleles)) 691 .collect(Collectors.toMap(EnumGene::allele, identity())); 692 693 return InvertibleCodec.of( 694 Genotype.of(new PermutationChromosome<>(ISeq.of(genes.values()))), 695 gt -> gt.chromosome().stream() 696 .map(EnumGene::allele) 697 .collect(ISeq.toISeq()), 698 val -> Genotype.of( 699 new PermutationChromosome<>( 700 val.stream() 701 .map(genes::get) 702 .collect(ISeq.toISeq()) 703 ) 704 ) 705 ); 706 } 707 708 /** 709 * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range. 710 * All matrix values are restricted by the same domain. The dimension of the 711 * returned matrix is {@code int[rows][cols]}. 712 * 713 * @since 4.4 714 * 715 * @param domain the domain of the matrix values 716 * @param rows the number of rows of the matrix 717 * @param cols the number of columns of the matrix 718 * @return a new matrix {@code Codec} 719 * @throws NullPointerException if the given {@code domain} is {@code null} 720 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are 721 * smaller than one. 722 */ 723 public static InvertibleCodec<int[][], IntegerGene> ofMatrix( 724 final IntRange domain, 725 final int rows, 726 final int cols 727 ) { 728 requireNonNull(domain); 729 Requires.positive(rows); 730 Requires.positive(cols); 731 732 return InvertibleCodec.of( 733 Genotype.of( 734 IntegerChromosome.of(domain, cols).instances() 735 .limit(rows) 736 .collect(ISeq.toISeq()) 737 ), 738 gt -> gt.stream() 739 .map(ch -> ch.as(IntegerChromosome.class).toArray()) 740 .toArray(int[][]::new), 741 matrix -> Genotype.of( 742 Stream.of(matrix) 743 .map(row -> 744 IntegerChromosome.of( 745 IntStream.of(row) 746 .mapToObj(v -> IntegerGene.of(v, domain)) 747 .collect(ISeq.toISeq()) 748 )) 749 .collect(ISeq.toISeq()) 750 ) 751 ); 752 } 753 754 /** 755 * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range. 756 * All matrix values are restricted by the same domain. The dimension of the 757 * returned matrix is {@code long[rows][cols]}. 758 * 759 * @since 4.4 760 * 761 * @param domain the domain of the matrix values 762 * @param rows the number of rows of the matrix 763 * @param cols the number of columns of the matrix 764 * @return a new matrix {@code Codec} 765 * @throws NullPointerException if the given {@code domain} is {@code null} 766 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are 767 * smaller than one. 768 */ 769 public static InvertibleCodec<long[][], LongGene> ofMatrix( 770 final LongRange domain, 771 final int rows, 772 final int cols 773 ) { 774 requireNonNull(domain); 775 Requires.positive(rows); 776 Requires.positive(cols); 777 778 return InvertibleCodec.of( 779 Genotype.of( 780 LongChromosome.of(domain, cols).instances() 781 .limit(rows) 782 .collect(ISeq.toISeq()) 783 ), 784 gt -> gt.stream() 785 .map(ch -> ch.as(LongChromosome.class).toArray()) 786 .toArray(long[][]::new), 787 matrix -> Genotype.of( 788 Stream.of(matrix) 789 .map(row -> 790 LongChromosome.of( 791 LongStream.of(row) 792 .mapToObj(v -> LongGene.of(v, domain)) 793 .collect(ISeq.toISeq()) 794 )) 795 .collect(ISeq.toISeq()) 796 ) 797 ); 798 } 799 800 /** 801 * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range. 802 * All matrix values are restricted by the same domain. The dimension of the 803 * returned matrix is {@code double[rows][cols]}. 804 * 805 * @since 4.4 806 * 807 * @param domain the domain of the matrix values 808 * @param rows the number of rows of the matrix 809 * @param cols the number of columns of the matrix 810 * @return a new matrix {@code Codec} 811 * @throws NullPointerException if the given {@code domain} is {@code null} 812 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are 813 * smaller than one. 814 */ 815 public static InvertibleCodec<double[][], DoubleGene> ofMatrix( 816 final DoubleRange domain, 817 final int rows, 818 final int cols 819 ) { 820 requireNonNull(domain); 821 Requires.positive(rows); 822 Requires.positive(cols); 823 824 return InvertibleCodec.of( 825 Genotype.of( 826 DoubleChromosome.of(domain, cols).instances() 827 .limit(rows) 828 .collect(ISeq.toISeq()) 829 ), 830 gt -> gt.stream() 831 .map(ch -> ch.as(DoubleChromosome.class).toArray()) 832 .toArray(double[][]::new), 833 matrix -> Genotype.of( 834 Stream.of(matrix) 835 .map(row -> 836 DoubleChromosome.of( 837 DoubleStream.of(row) 838 .mapToObj(v -> DoubleGene.of(v, domain)) 839 .collect(ISeq.toISeq()) 840 )) 841 .collect(ISeq.toISeq()) 842 ) 843 ); 844 } 845 846 /** 847 * Create a codec, which creates a mapping from the elements given in the 848 * {@code source} sequence to the elements given in the {@code target} 849 * sequence. The returned mapping can be seen as a function which maps every 850 * element of the {@code target} set to an element of the {@code source} set. 851 * {@snippet lang="java": 852 * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5); 853 * final ISeq<String> strings = ISeq.of("1", "2", "3"); 854 * 855 * final Codec<Map<Integer, String>, EnumGene<Integer>> codec = 856 * Codecs.ofMapping(numbers, strings, HashMap::new); 857 * } 858 * 859 * If {@code source.size() > target.size()}, the created mapping is 860 * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>, 861 * if {@code source.size() < target.size()}, the mapping is 862 * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a> 863 * and if both sets have the same size, the returned mapping is 864 * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>. 865 * 866 * @since 4.3 867 * 868 * @param source the source elements. Will be the <em>keys</em> of the 869 * encoded {@code Map}. 870 * @param target the target elements. Will be the <em>values</em> of the 871 * encoded {@code Map}. 872 * @param mapSupplier a function which returns a new, empty Map into which 873 * the mapping will be inserted 874 * @param <A> the type of the source elements 875 * @param <B> the type of the target elements 876 * @param <M> the type of the encoded Map 877 * @return a new mapping codec 878 * @throws IllegalArgumentException if the {@code target} sequences are empty 879 * @throws NullPointerException if one of the arguments is {@code null} 880 */ 881 public static <A, B, M extends Map<A, B>> InvertibleCodec<M, EnumGene<Integer>> 882 ofMapping( 883 final ISeq<? extends A> source, 884 final ISeq<? extends B> target, 885 final Supplier<M> mapSupplier 886 ) { 887 requireNonNull(source); 888 requireNonNull(target); 889 requireNonNull(mapSupplier); 890 891 final Map<A, Integer> smap = IntStream.range(0, source.length()) 892 .mapToObj(i -> entry(source.get(i), i)) 893 .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 894 895 final Map<B, Integer> tmap = IntStream.range(0, target.length()) 896 .mapToObj(i -> entry(target.get(i), i)) 897 .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 898 899 final PermutationChromosome<Integer> chromosome = 900 PermutationChromosome.ofInteger(target.size()); 901 902 final Map<Integer, EnumGene<Integer>> genes = chromosome.stream() 903 .collect(Collectors.toMap(EnumGene::allele, identity())); 904 905 final Codec<int[], EnumGene<Integer>> codec = Codec.of( 906 Genotype.of(chromosome), 907 gt -> gt.chromosome().stream() 908 .mapToInt(EnumGene::allele) 909 .toArray() 910 ); 911 912 return codec 913 .map(permutation -> toMapping(permutation, source, target, mapSupplier)) 914 .toInvertibleCodec(mapping -> toEncoding(mapping, smap,tmap, genes)); 915 } 916 917 private static <A, B, M extends Map<A, B>> M toMapping( 918 final int[] perm, 919 final ISeq<? extends A> source, 920 final ISeq<? extends B> target, 921 final Supplier<M> mapSupplier 922 ) { 923 return IntStream.range(0, source.size()) 924 .mapToObj(i -> entry(source.get(i), target.get(perm[i%perm.length]))) 925 .collect(Collectors.toMap( 926 Entry::getKey, Entry::getValue, 927 (u,v) -> {throw new IllegalStateException("Duplicate key " + u);}, 928 mapSupplier)); 929 } 930 931 private static <A, B> Genotype<EnumGene<Integer>> toEncoding( 932 final Map<A, B> mapping, 933 final Map<A, Integer> source, 934 final Map<B, Integer> target, 935 final Map<Integer, EnumGene<Integer>> genes 936 ) { 937 final int[] perm = new int[target.size()]; 938 source.forEach((key, value) -> { 939 final int i = value; 940 final int j = target.get(mapping.get(key)); 941 perm[i%perm.length] = j; 942 }); 943 944 // If the target size is greater the source size, only the first 945 // elements (source size) are filled. The rest of the 'perm' array 946 // has to be filled with unique elements. 947 if (target.size() > source.size()) { 948 final int[] indexes = new int[target.size()]; 949 950 // Initialize the index set with all target indexes: O(|t|) 951 for (int i = 0; i < target.size(); ++i) { 952 indexes[i] = i; 953 } 954 955 // Mark existing permutations in the index array: O(|s|*log(|t|)) 956 for (int i = 0; i < source.size(); ++i) { 957 final int si = Arrays.binarySearch(indexes, perm[i]); 958 if (si >= 0) { 959 indexes[si] = -1; 960 } 961 } 962 963 // Fill the 'perm' array with the remaining, non-duplicate indexes: 964 // O(|t|) 965 int j = 0; 966 int next; 967 for (int i = source.size(); i < target.size(); ++i) { 968 while ((next = indexes[j++]) == -1); 969 perm[i] = next; 970 } 971 } 972 973 return Genotype.of( 974 new PermutationChromosome<>( 975 IntStream.of(perm) 976 .mapToObj(genes::get) 977 .collect(ISeq.toISeq()) 978 ) 979 ); 980 } 981 982 /** 983 * Create a codec, which creates a mapping from the elements given in the 984 * {@code source} sequence to the elements given in the {@code target} 985 * sequence. The returned mapping can be seen as a function which maps every 986 * element of the {@code target} set to an element of the {@code source} set. 987 * {@snippet lang="java": 988 * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5); 989 * final ISeq<String> strings = ISeq.of("1", "2", "3"); 990 * 991 * final Codec<Map<Integer, String>, EnumGene<Integer>> codec = 992 * Codecs.ofMapping(numbers, strings); 993 * } 994 * 995 * If {@code source.size() > target.size()}, the created mapping is 996 * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>, 997 * if {@code source.size() < target.size()}, the mapping is 998 * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a> 999 * and if both sets have the same size, the returned mapping is 1000 * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>. 1001 * 1002 * @since 4.3 1003 * 1004 * @param source the source elements. Will be the <em>keys</em> of the 1005 * encoded {@code Map}. 1006 * @param target the target elements. Will be the <em>values</em> of the 1007 * encoded {@code Map}. 1008 * @param <A> the type of the source elements 1009 * @param <B> the type of the target elements 1010 * @return a new mapping codec 1011 * @throws IllegalArgumentException if the {@code target} sequences are empty 1012 * @throws NullPointerException if one of the arguments is {@code null} 1013 */ 1014 public static <A, B> InvertibleCodec<Map<A, B>, EnumGene<Integer>> 1015 ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) { 1016 return ofMapping(source, target, HashMap::new); 1017 } 1018 1019 /** 1020 * The subset {@link InvertibleCodec} can be used for problems where it is 1021 * required to find the best <b>variable-sized</b> subset from a given basic 1022 * set. A typical usage example of the returned {@code Codec} is the 1023 * Knapsack problem. 1024 * <p> 1025 * The following code snippet shows a simplified variation of the Knapsack 1026 * problem. 1027 * {@snippet lang="java": 1028 * public final class Main { 1029 * // The basic set from where to choose an 'optimal' subset. 1030 * private final static ISeq<Integer> SET = 1031 * ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 1032 * 1033 * // Fitness function directly takes an 'int' value. 1034 * private static int fitness(final ISeq<Integer> subset) { 1035 * assert(subset.size() <= SET.size()); 1036 * final int size = subset.stream() 1037 * .mapToInt(Integer::intValue) 1038 * .sum(); 1039 * return size <= 20 ? size : 0; 1040 * } 1041 * 1042 * public static void main(final String[] args) { 1043 * final Engine<BitGene, Double> engine = Engine 1044 * .builder(Main::fitness, codec.ofSubSet(SET)) 1045 * .build(); 1046 * // ... 1047 * } 1048 * } 1049 * } 1050 * 1051 * @param <T> the element type of the basic set 1052 * @param basicSet the basic set, from where to choose the <i>optimal</i> 1053 * subset. 1054 * @return a new codec which can be used for modelling <i>subset</i> 1055 * problems. 1056 * @throws NullPointerException if the given {@code basicSet} is 1057 * {@code null}; {@code null} elements are allowed. 1058 * @throws IllegalArgumentException if the {@code basicSet} size is smaller 1059 * than one. 1060 */ 1061 public static <T> InvertibleCodec<ISeq<T>, BitGene> 1062 ofSubSet(final ISeq<? extends T> basicSet) { 1063 requireNonNull(basicSet); 1064 Requires.positive(basicSet.length()); 1065 1066 return InvertibleCodec.of( 1067 Genotype.of(BitChromosome.of(basicSet.length())), 1068 gt -> gt.chromosome() 1069 .as(BitChromosome.class).ones() 1070 .<T>mapToObj(basicSet) 1071 .collect(ISeq.toISeq()), 1072 values -> { 1073 final byte[] bits = Bits.newArray(basicSet.size()); 1074 1075 int i = 0; 1076 for (T v : values) { 1077 while (i < basicSet.size() && !Objects.equals(basicSet.get(i), v)) { 1078 ++i; 1079 } 1080 if (i >= basicSet.size()) { 1081 throw new IllegalArgumentException( 1082 "Invalid subset; input values were not created by " + 1083 "'decode' method." 1084 ); 1085 } 1086 1087 Bits.set(bits, i); 1088 } 1089 1090 return Genotype.of(new BitChromosome(bits, 0, basicSet.size())); 1091 } 1092 ); 1093 } 1094 1095 /** 1096 * The subset {@link InvertibleCodec} can be used for problems where it is 1097 * required to find the best <b>fixed-size</b> subset from a given basic set. 1098 * 1099 * @since 3.4 1100 * 1101 * @see PermutationChromosome 1102 * @see PermutationChromosome#of(ISeq, int) 1103 * 1104 * @param <T> the element type of the basic set 1105 * @param basicSet the basic set, from where to choose the <i>optimal</i> 1106 * subset. 1107 * @param size the length of the desired subsets 1108 * @return a new codec which can be used for modelling <i>subset</i> 1109 * problems. 1110 * @throws NullPointerException if the given {@code basicSet} is 1111 * {@code null}; {@code null} elements are allowed. 1112 * @throws IllegalArgumentException if {@code basicSet.size() < size}, 1113 * {@code size <= 0} or {@code basicSet.size()*size} will cause an 1114 * integer overflow. 1115 */ 1116 public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> ofSubSet( 1117 final ISeq<? extends T> basicSet, 1118 final int size 1119 ) { 1120 requireNonNull(basicSet); 1121 1122 final Map<T, EnumGene<T>> genes = 1123 IntStream.range(0, basicSet.length()) 1124 .mapToObj(i -> EnumGene.<T>of(i, basicSet)) 1125 .collect(Collectors.toMap(EnumGene::allele, identity())); 1126 1127 return InvertibleCodec.of( 1128 Genotype.of(PermutationChromosome.of(basicSet, size)), 1129 gt -> gt.chromosome().stream() 1130 .map(EnumGene::allele) 1131 .collect(ISeq.toISeq()), 1132 values -> { 1133 if (values.size() != size) { 1134 throw new IllegalArgumentException(format( 1135 "Expected sub-set size of %d, but got %d,", 1136 size, values.size() 1137 )); 1138 } 1139 1140 return Genotype.of( 1141 new PermutationChromosome<>( 1142 values.stream() 1143 .map(genes::get) 1144 .collect(ISeq.toISeq()) 1145 ) 1146 ); 1147 } 1148 ); 1149 } 1150 1151// /** 1152// * Creates a codec for a 2-dimensional affine transformation. The composed 1153// * order of the transformation is: <i>R•Sc•Sh•T</i> 1154// * 1155// * @param sx the scale factor range in x direction 1156// * @param sy the scale factor range in y direction 1157// * @param tx the translation range in x direction 1158// * @param ty the translation range in y direction 1159// * @param th the rotation range (in radians) 1160// * @param kx the shear range in x direction 1161// * @param ky the shear range in x direction 1162// * @return the affine transformation codec 1163// * @throws NullPointerException if one of the arguments is {@code null} 1164// */ 1165// static Codec<AffineTransform, DoubleGene> ofAffineTransform( 1166// final DoubleRange sx, final DoubleRange sy, 1167// final DoubleRange tx, final DoubleRange ty, 1168// final DoubleRange th, 1169// final DoubleRange kx, final DoubleRange ky 1170// ) { 1171// return Codec.of( 1172// Genotype.of( 1173// // Scale 1174// DoubleChromosome.of(sx), DoubleChromosome.of(sy), 1175// // Translation 1176// DoubleChromosome.of(tx), DoubleChromosome.of(ty), 1177// // Rotation 1178// DoubleChromosome.of(th), 1179// // Shear 1180// DoubleChromosome.of(kx), DoubleChromosome.of(ky) 1181// ), 1182// gt -> { 1183// final AffineTransform at = new AffineTransform(); 1184// 1185// at.translate( 1186// gt.getChromosome(2).getGene().doubleValue(), 1187// gt.getChromosome(3).getGene().doubleValue() 1188// ); 1189// at.shear( 1190// gt.getChromosome(5).getGene().doubleValue(), 1191// gt.getChromosome(6).getGene().doubleValue() 1192// ); 1193// at.scale( 1194// gt.getChromosome(0).getGene().doubleValue(), 1195// gt.getChromosome(1).getGene().doubleValue() 1196// ); 1197// at.rotate(gt.getChromosome(4).getGene().doubleValue()); 1198// 1199// return at; 1200// } 1201// ); 1202// } 1203// 1204// /** 1205// * Creates a codec for a 2-dimensional affine transformation. The composed 1206// * order of the transformation is: <i>R•Sc•Sh•T</i> 1207// * 1208// * @param s the scale factor range in x and y direction 1209// * @param t the translation range in x and y direction 1210// * @param th the rotation angle range 1211// * @param k the shear range in x and y direction 1212// * @return the affine transformation codec 1213// * @throws NullPointerException if one of the arguments is {@code null} 1214// */ 1215// static Codec<AffineTransform, DoubleGene> ofAffineTransform( 1216// final DoubleRange s, 1217// final DoubleRange t, 1218// final DoubleRange th, 1219// final DoubleRange k 1220// ) { 1221// return ofAffineTransform(s, s, t, t, th, k, k); 1222// } 1223 1224}