001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 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 * <pre>{@code 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 * }</pre> 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, IntRange.of(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, IntRange.of(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. 297 * 298 * @param domain the domain of the vector values 299 * @param length the vector length 300 * @return a new vector {@code Codec} 301 * @throws NullPointerException if the given {@code domain} is {@code null} 302 * @throws IllegalArgumentException if the {@code length} is smaller than 303 * one. 304 */ 305 public static InvertibleCodec<double[], DoubleGene> ofVector( 306 final DoubleRange domain, 307 final int length 308 ) { 309 return ofVector(domain, IntRange.of(length)); 310 } 311 312 /** 313 * Return a vector {@link InvertibleCodec} for the given range. All vector 314 * values are restricted by the same domain. 315 * 316 * @since 7.0 317 * 318 * @param domain the domain of the vector values 319 * @param length the vector length range 320 * @return a new vector {@code Codec} 321 * @throws NullPointerException if the given {@code domain} is {@code null} 322 * @throws IllegalArgumentException if the {@code length} is smaller than 323 * one. 324 */ 325 public static InvertibleCodec<double[], DoubleGene> ofVector( 326 final DoubleRange domain, 327 final IntRange length 328 ) { 329 requireNonNull(domain); 330 Requires.positive(length.min()); 331 332 return InvertibleCodec.of( 333 Genotype.of(DoubleChromosome.of(domain, length)), 334 gt -> gt.chromosome().as(DoubleChromosome.class).toArray(), 335 val -> Genotype.of( 336 DoubleChromosome.of( 337 DoubleStream.of(val) 338 .mapToObj(d -> DoubleGene.of(d, domain)) 339 .collect(ISeq.toISeq()) 340 ) 341 ) 342 ); 343 } 344 345 /** 346 * Create a vector {@link InvertibleCodec} for the given ranges. Each vector 347 * element might have a different domain. The vector length is equal to the 348 * number of domains. 349 * 350 * @param domains the domain ranges 351 * @return a new vector {@code Codec} 352 * @throws NullPointerException if one of the arguments is {@code null} 353 * @throws IllegalArgumentException if the {@code domains} array is empty 354 */ 355 public static InvertibleCodec<int[], IntegerGene> 356 ofVector(final IntRange... domains) { 357 if (domains.length == 0) { 358 throw new IllegalArgumentException("Domains must not be empty."); 359 } 360 361 final ISeq<IntegerChromosome> chromosomes = Stream.of(domains) 362 .peek(Objects::requireNonNull) 363 .map(IntegerGene::of) 364 .map(IntegerChromosome::of) 365 .collect(ISeq.toISeq()); 366 367 return InvertibleCodec.of( 368 Genotype.of(chromosomes), 369 gt -> { 370 final int[] args = new int[gt.length()]; 371 for (int i = gt.length(); --i >= 0;) { 372 args[i] = gt.get(i).gene().intValue(); 373 } 374 return args; 375 }, 376 val -> Genotype.of( 377 IntStream.range(0, val.length) 378 .mapToObj(i -> 379 IntegerChromosome.of(IntegerGene.of(val[i], domains[i]))) 380 .collect(ISeq.toISeq()) 381 ) 382 ); 383 } 384 385 /** 386 * Create a vector {@link InvertibleCodec} for the given ranges. Each vector 387 * element might have a different domain. The vector length is equal to the 388 * number of domains. 389 * 390 * @param domains the domain ranges 391 * @return a new vector {@code Codec} 392 * @throws NullPointerException if one of the arguments is {@code null} 393 * @throws IllegalArgumentException if the {@code domains} array is empty 394 */ 395 public static InvertibleCodec<long[], LongGene> 396 ofVector(final LongRange... domains) { 397 if (domains.length == 0) { 398 throw new IllegalArgumentException("Domains must not be empty."); 399 } 400 401 final ISeq<LongChromosome> chromosomes = Stream.of(domains) 402 .peek(Objects::requireNonNull) 403 .map(LongGene::of) 404 .map(LongChromosome::of) 405 .collect(ISeq.toISeq()); 406 407 return InvertibleCodec.of( 408 Genotype.of(chromosomes), 409 gt -> { 410 final long[] args = new long[gt.length()]; 411 for (int i = gt.length(); --i >= 0;) { 412 args[i] = gt.get(i).gene().longValue(); 413 } 414 return args; 415 }, 416 val -> Genotype.of( 417 IntStream.range(0, val.length) 418 .mapToObj(i -> 419 LongChromosome.of(LongGene.of(val[i], domains[i]))) 420 .collect(ISeq.toISeq()) 421 ) 422 ); 423 } 424 425 /** 426 * Create a vector {@link InvertibleCodec} for the given ranges. Each vector 427 * element might have a different domain. The vector length is equal to the 428 * number of domains. 429 * 430 * @param domains the domain ranges 431 * @return a new vector {@code Codec} 432 * @throws NullPointerException if one of the arguments is {@code null} 433 * @throws IllegalArgumentException if the {@code domains} array is empty 434 */ 435 public static InvertibleCodec<double[], DoubleGene> 436 ofVector(final DoubleRange... domains) { 437 if (domains.length == 0) { 438 throw new IllegalArgumentException("Domains must not be empty."); 439 } 440 441 final ISeq<DoubleChromosome> chromosomes = Stream.of(domains) 442 .peek(Objects::requireNonNull) 443 .map(DoubleGene::of) 444 .map(DoubleChromosome::of) 445 .collect(ISeq.toISeq()); 446 447 return InvertibleCodec.of( 448 Genotype.of(chromosomes), 449 gt -> { 450 final double[] args = new double[gt.length()]; 451 for (int i = gt.length(); --i >= 0;) { 452 args[i] = gt.get(i).gene().doubleValue(); 453 } 454 return args; 455 }, 456 val -> Genotype.of( 457 IntStream.range(0, val.length) 458 .mapToObj(i -> 459 DoubleChromosome.of(DoubleGene.of(val[i], domains[i]))) 460 .collect(ISeq.toISeq()) 461 ) 462 ); 463 } 464 465 /** 466 * Return a scala {@link Codec} with the given allele {@link Supplier}, 467 * allele {@code validator} and {@code Chromosome} length. The 468 * {@code supplier} is responsible for creating new random alleles, and the 469 * {@code validator} can verify it. 470 * <p> 471 * The following example shows a codec which creates and verifies 472 * {@code BigInteger} object arrays. 473 * <pre>{@code 474 * final Codec<BigInteger[], AnyGene<BigInteger>> codec = Codecs.of( 475 * // Create new random 'BigInteger' object. 476 * () -> { 477 * final byte[] data = new byte[100]; 478 * RandomRegistry.getRandom().nextBytes(data); 479 * return new BigInteger(data); 480 * }, 481 * // Verify that bit 7 is set. (For illustration purpose.) 482 * bi -> bi.testBit(7), 483 * // The 'Chromosome' length. 484 * 123 485 * ); 486 * }</pre> 487 * 488 * @see AnyChromosome#of(Supplier, Predicate, Predicate, int) 489 * 490 * @param <A> the allele type 491 * @param supplier the allele-supplier which is used for creating new, 492 * random alleles 493 * @param alleleValidator the validator used for validating the created gene. 494 * This predicate is used in the {@link AnyGene#isValid()} method. 495 * @param alleleSeqValidator the validator used for validating the created 496 * chromosome. This predicate is used in the 497 * {@link AnyChromosome#isValid()} method. 498 * @param length the vector length 499 * @return a new {@code Codec} with the given parameters 500 * @throws NullPointerException if one of the parameters is {@code null} 501 * @throws IllegalArgumentException if the length of the vector is smaller 502 * than one. 503 */ 504 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector( 505 final Supplier<? extends A> supplier, 506 final Predicate<? super A> alleleValidator, 507 final Predicate<? super ISeq<A>> alleleSeqValidator, 508 final int length 509 ) { 510 requireNonNull(supplier); 511 requireNonNull(alleleSeqValidator); 512 requireNonNull(alleleSeqValidator); 513 Requires.positive(length); 514 515 return Codec.of( 516 Genotype.of(AnyChromosome 517 .of(supplier, alleleValidator, alleleSeqValidator, length)), 518 gt -> gt.chromosome().stream() 519 .map(Gene::allele) 520 .collect(ISeq.toISeq()) 521 ); 522 } 523 524 /** 525 * Return a scala {@link Codec} with the given allele {@link Supplier}, 526 * allele {@code validator} and {@code Chromosome} length. The 527 * {@code supplier} is responsible for creating new random alleles, and the 528 * {@code validator} can verify it. 529 * 530 * @param <A> the allele type 531 * @param supplier the allele-supplier which is used for creating new, 532 * random alleles 533 * @param validator the validator used for validating the created gene. This 534 * predicate is used in the {@link AnyGene#isValid()} method. 535 * @param length the vector length 536 * @return a new {@code Codec} with the given parameters 537 * @throws NullPointerException if one of the parameters is {@code null} 538 * @throws IllegalArgumentException if the length of the vector is smaller 539 * than one. 540 */ 541 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector( 542 final Supplier<? extends A> supplier, 543 final Predicate<? super A> validator, 544 final int length 545 ) { 546 return ofVector( 547 supplier, 548 validator, 549 Predicates.True(), 550 length 551 ); 552 } 553 554 /** 555 * Return a scala {@link Codec} with the given allele {@link Supplier} and 556 * {@code Chromosome} length. The {@code supplier} is responsible for 557 * creating new random alleles. 558 * 559 * @param <A> the allele type 560 * @param supplier the allele-supplier which is used for creating new, 561 * random alleles 562 * @param length the vector length 563 * @return a new {@code Codec} with the given parameters 564 * @throws NullPointerException if one of the parameters is {@code null} 565 * @throws IllegalArgumentException if the length of the vector is smaller 566 * than one. 567 */ 568 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector( 569 final Supplier<? extends A> supplier, 570 final int length 571 ) { 572 return ofVector(supplier, Predicates.TRUE, length); 573 } 574 575 /** 576 * Create a permutation {@link InvertibleCodec} of integer in the range 577 * {@code [0, length)}. 578 * 579 * @param length the number of permutation elements 580 * @return a permutation {@code Codec} of integers 581 * @throws IllegalArgumentException if the {@code length} is smaller than 582 * one. 583 */ 584 public static InvertibleCodec<int[], EnumGene<Integer>> 585 ofPermutation(final int length) { 586 Requires.positive(length); 587 588 final PermutationChromosome<Integer> chromosome = 589 PermutationChromosome.ofInteger(length); 590 591 final Map<Integer, EnumGene<Integer>> genes = chromosome.stream() 592 .collect(Collectors.toMap(EnumGene::allele, identity())); 593 594 return InvertibleCodec.of( 595 Genotype.of(chromosome), 596 gt -> gt.chromosome().stream() 597 .mapToInt(EnumGene::allele) 598 .toArray(), 599 val -> Genotype.of( 600 new PermutationChromosome<>( 601 IntStream.of(val) 602 .mapToObj(genes::get) 603 .collect(ISeq.toISeq()) 604 ) 605 ) 606 ); 607 } 608 609 /** 610 * Create a permutation {@link InvertibleCodec} with the given alleles. 611 * 612 * @param alleles the alleles of the permutation 613 * @param <T> the allele type 614 * @return a new permutation {@code Codec} 615 * @throws IllegalArgumentException if the given allele array is empty 616 * @throws NullPointerException if one of the alleles is {@code null} 617 */ 618 public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> 619 ofPermutation(final ISeq<? extends T> alleles) { 620 if (alleles.isEmpty()) { 621 throw new IllegalArgumentException( 622 "Empty allele array is not allowed." 623 ); 624 } 625 626 final Map<T, EnumGene<T>> genes = 627 IntStream.range(0, alleles.length()) 628 .mapToObj(i -> EnumGene.<T>of(i, alleles)) 629 .collect(Collectors.toMap(EnumGene::allele, identity())); 630 631 return InvertibleCodec.of( 632 Genotype.of(new PermutationChromosome<>(ISeq.of(genes.values()))), 633 gt -> gt.chromosome().stream() 634 .map(EnumGene::allele) 635 .collect(ISeq.toISeq()), 636 val -> Genotype.of( 637 new PermutationChromosome<>( 638 val.stream() 639 .map(genes::get) 640 .collect(ISeq.toISeq()) 641 ) 642 ) 643 ); 644 } 645 646 /** 647 * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range. 648 * All matrix values are restricted by the same domain. The dimension of the 649 * returned matrix is {@code int[rows][cols]}. 650 * 651 * @since 4.4 652 * 653 * @param domain the domain of the matrix values 654 * @param rows the number of rows of the matrix 655 * @param cols the number of columns of the matrix 656 * @return a new matrix {@code Codec} 657 * @throws NullPointerException if the given {@code domain} is {@code null} 658 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are 659 * smaller than one. 660 */ 661 public static InvertibleCodec<int[][], IntegerGene> ofMatrix( 662 final IntRange domain, 663 final int rows, 664 final int cols 665 ) { 666 requireNonNull(domain); 667 Requires.positive(rows); 668 Requires.positive(cols); 669 670 return InvertibleCodec.of( 671 Genotype.of( 672 IntegerChromosome.of(domain, cols).instances() 673 .limit(rows) 674 .collect(ISeq.toISeq()) 675 ), 676 gt -> gt.stream() 677 .map(ch -> ch.as(IntegerChromosome.class).toArray()) 678 .toArray(int[][]::new), 679 matrix -> Genotype.of( 680 Stream.of(matrix) 681 .map(row -> 682 IntegerChromosome.of( 683 IntStream.of(row) 684 .mapToObj(v -> IntegerGene.of(v, domain)) 685 .collect(ISeq.toISeq()) 686 )) 687 .collect(ISeq.toISeq()) 688 ) 689 ); 690 } 691 692 /** 693 * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range. 694 * All matrix values are restricted by the same domain. The dimension of the 695 * returned matrix is {@code long[rows][cols]}. 696 * 697 * @since 4.4 698 * 699 * @param domain the domain of the matrix values 700 * @param rows the number of rows of the matrix 701 * @param cols the number of columns of the matrix 702 * @return a new matrix {@code Codec} 703 * @throws NullPointerException if the given {@code domain} is {@code null} 704 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are 705 * smaller than one. 706 */ 707 public static InvertibleCodec<long[][], LongGene> ofMatrix( 708 final LongRange domain, 709 final int rows, 710 final int cols 711 ) { 712 requireNonNull(domain); 713 Requires.positive(rows); 714 Requires.positive(cols); 715 716 return InvertibleCodec.of( 717 Genotype.of( 718 LongChromosome.of(domain, cols).instances() 719 .limit(rows) 720 .collect(ISeq.toISeq()) 721 ), 722 gt -> gt.stream() 723 .map(ch -> ch.as(LongChromosome.class).toArray()) 724 .toArray(long[][]::new), 725 matrix -> Genotype.of( 726 Stream.of(matrix) 727 .map(row -> 728 LongChromosome.of( 729 LongStream.of(row) 730 .mapToObj(v -> LongGene.of(v, domain)) 731 .collect(ISeq.toISeq()) 732 )) 733 .collect(ISeq.toISeq()) 734 ) 735 ); 736 } 737 738 /** 739 * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range. 740 * All matrix values are restricted by the same domain. The dimension of the 741 * returned matrix is {@code double[rows][cols]}. 742 * 743 * @since 4.4 744 * 745 * @param domain the domain of the matrix values 746 * @param rows the number of rows of the matrix 747 * @param cols the number of columns of the matrix 748 * @return a new matrix {@code Codec} 749 * @throws NullPointerException if the given {@code domain} is {@code null} 750 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are 751 * smaller than one. 752 */ 753 public static InvertibleCodec<double[][], DoubleGene> ofMatrix( 754 final DoubleRange domain, 755 final int rows, 756 final int cols 757 ) { 758 requireNonNull(domain); 759 Requires.positive(rows); 760 Requires.positive(cols); 761 762 return InvertibleCodec.of( 763 Genotype.of( 764 DoubleChromosome.of(domain, cols).instances() 765 .limit(rows) 766 .collect(ISeq.toISeq()) 767 ), 768 gt -> gt.stream() 769 .map(ch -> ch.as(DoubleChromosome.class).toArray()) 770 .toArray(double[][]::new), 771 matrix -> Genotype.of( 772 Stream.of(matrix) 773 .map(row -> 774 DoubleChromosome.of( 775 DoubleStream.of(row) 776 .mapToObj(v -> DoubleGene.of(v, domain)) 777 .collect(ISeq.toISeq()) 778 )) 779 .collect(ISeq.toISeq()) 780 ) 781 ); 782 } 783 784 /** 785 * Create a codec, which creates a mapping from the elements given in the 786 * {@code source} sequence to the elements given in the {@code target} 787 * sequence. The returned mapping can be seen as a function which maps every 788 * element of the {@code target} set to an element of the {@code source} set. 789 * 790 * <pre>{@code 791 * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5); 792 * final ISeq<String> strings = ISeq.of("1", "2", "3"); 793 * 794 * final Codec<Map<Integer, String>, EnumGene<Integer>> codec = 795 * Codecs.ofMapping(numbers, strings, HashMap::new); 796 * }</pre> 797 * 798 * If {@code source.size() > target.size()}, the created mapping is 799 * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>, 800 * if {@code source.size() < target.size()}, the mapping is 801 * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a> 802 * and if both sets have the same size, the returned mapping is 803 * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>. 804 * 805 * @since 4.3 806 * 807 * @param source the source elements. Will be the <em>keys</em> of the 808 * encoded {@code Map}. 809 * @param target the target elements. Will be the <em>values</em> of the 810 * encoded {@code Map}. 811 * @param mapSupplier a function which returns a new, empty Map into which 812 * the mapping will be inserted 813 * @param <A> the type of the source elements 814 * @param <B> the type of the target elements 815 * @param <M> the type of the encoded Map 816 * @return a new mapping codec 817 * @throws IllegalArgumentException if the {@code target} sequences are empty 818 * @throws NullPointerException if one of the arguments is {@code null} 819 */ 820 public static <A, B, M extends Map<A, B>> InvertibleCodec<M, EnumGene<Integer>> 821 ofMapping( 822 final ISeq<? extends A> source, 823 final ISeq<? extends B> target, 824 final Supplier<M> mapSupplier 825 ) { 826 requireNonNull(source); 827 requireNonNull(target); 828 requireNonNull(mapSupplier); 829 830 final Map<A, Integer> smap = IntStream.range(0, source.length()) 831 .mapToObj(i -> entry(source.get(i), i)) 832 .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 833 834 final Map<B, Integer> tmap = IntStream.range(0, target.length()) 835 .mapToObj(i -> entry(target.get(i), i)) 836 .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 837 838 final PermutationChromosome<Integer> chromosome = 839 PermutationChromosome.ofInteger(target.size()); 840 841 final Map<Integer, EnumGene<Integer>> genes = chromosome.stream() 842 .collect(Collectors.toMap(EnumGene::allele, identity())); 843 844 final Codec<int[], EnumGene<Integer>> codec = Codec.of( 845 Genotype.of(chromosome), 846 gt -> gt.chromosome().stream() 847 .mapToInt(EnumGene::allele) 848 .toArray() 849 ); 850 851 return codec 852 .map(permutation -> toMapping(permutation, source, target, mapSupplier)) 853 .toInvertibleCodec(mapping -> toEncoding(mapping, smap,tmap, genes)); 854 } 855 856 private static <A, B, M extends Map<A, B>> M toMapping( 857 final int[] perm, 858 final ISeq<? extends A> source, 859 final ISeq<? extends B> target, 860 final Supplier<M> mapSupplier 861 ) { 862 return IntStream.range(0, source.size()) 863 .mapToObj(i -> entry(source.get(i), target.get(perm[i%perm.length]))) 864 .collect(Collectors.toMap( 865 Entry::getKey, Entry::getValue, 866 (u,v) -> {throw new IllegalStateException("Duplicate key " + u);}, 867 mapSupplier)); 868 } 869 870 private static <A, B> Genotype<EnumGene<Integer>> toEncoding( 871 final Map<A, B> mapping, 872 final Map<A, Integer> source, 873 final Map<B, Integer> target, 874 final Map<Integer, EnumGene<Integer>> genes 875 ) { 876 final int[] perm = new int[target.size()]; 877 source.forEach((key, value) -> { 878 final int i = value; 879 final int j = target.get(mapping.get(key)); 880 perm[i%perm.length] = j; 881 }); 882 883 // If the target size is greater the source size, only the first 884 // elements (source size) are filled. The rest of the 'perm' array 885 // has to be filled with unique elements. 886 if (target.size() > source.size()) { 887 final int[] indexes = new int[target.size()]; 888 889 // Initialize the index set with all target indexes: O(|t|) 890 for (int i = 0; i < target.size(); ++i) { 891 indexes[i] = i; 892 } 893 894 // Mark existing permutations in the index array: O(|s|*log(|t|)) 895 for (int i = 0; i < source.size(); ++i) { 896 final int si = Arrays.binarySearch(indexes, perm[i]); 897 if (si >= 0) { 898 indexes[si] = -1; 899 } 900 } 901 902 // Fill the 'perm' array with the remaining, non-duplicate indexes: 903 // O(|t|) 904 int j = 0; 905 int next; 906 for (int i = source.size(); i < target.size(); ++i) { 907 while ((next = indexes[j++]) == -1); 908 perm[i] = next; 909 } 910 } 911 912 return Genotype.of( 913 new PermutationChromosome<>( 914 IntStream.of(perm) 915 .mapToObj(genes::get) 916 .collect(ISeq.toISeq()) 917 ) 918 ); 919 } 920 921 /** 922 * Create a codec, which creates a mapping from the elements given in the 923 * {@code source} sequence to the elements given in the {@code target} 924 * sequence. The returned mapping can be seen as a function which maps every 925 * element of the {@code target} set to an element of the {@code source} set. 926 * 927 * <pre>{@code 928 * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5); 929 * final ISeq<String> strings = ISeq.of("1", "2", "3"); 930 * 931 * final Codec<Map<Integer, String>, EnumGene<Integer>> codec = 932 * Codecs.ofMapping(numbers, strings); 933 * }</pre> 934 * 935 * If {@code source.size() > target.size()}, the created mapping is 936 * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>, 937 * if {@code source.size() < target.size()}, the mapping is 938 * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a> 939 * and if both sets have the same size, the returned mapping is 940 * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>. 941 * 942 * @since 4.3 943 * 944 * @param source the source elements. Will be the <em>keys</em> of the 945 * encoded {@code Map}. 946 * @param target the target elements. Will be the <em>values</em> of the 947 * encoded {@code Map}. 948 * @param <A> the type of the source elements 949 * @param <B> the type of the target elements 950 * @return a new mapping codec 951 * @throws IllegalArgumentException if the {@code target} sequences are empty 952 * @throws NullPointerException if one of the arguments is {@code null} 953 */ 954 public static <A, B> InvertibleCodec<Map<A, B>, EnumGene<Integer>> 955 ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) { 956 return ofMapping(source, target, HashMap::new); 957 } 958 959 /** 960 * The subset {@link InvertibleCodec} can be used for problems where it is 961 * required to find the best <b>variable-sized</b> subset from a given basic 962 * set. A typical usage example of the returned {@code Codec} is the 963 * Knapsack problem. 964 * <p> 965 * The following code snippet shows a simplified variation of the Knapsack 966 * problem. 967 * <pre>{@code 968 * public final class Main { 969 * // The basic set from where to choose an 'optimal' subset. 970 * private final static ISeq<Integer> SET = 971 * ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 972 * 973 * // Fitness function directly takes an 'int' value. 974 * private static int fitness(final ISeq<Integer> subset) { 975 * assert(subset.size() <= SET.size()); 976 * final int size = subset.stream() 977 * .collect(Collectors.summingInt(Integer::intValue)); 978 * return size <= 20 ? size : 0; 979 * } 980 * 981 * public static void main(final String[] args) { 982 * final Engine<BitGene, Double> engine = Engine 983 * .builder(Main::fitness, codec.ofSubSet(SET)) 984 * .build(); 985 * ... 986 * } 987 * } 988 * }</pre> 989 * 990 * @param <T> the element type of the basic set 991 * @param basicSet the basic set, from where to choose the <i>optimal</i> 992 * subset. 993 * @return a new codec which can be used for modelling <i>subset</i> 994 * problems. 995 * @throws NullPointerException if the given {@code basicSet} is 996 * {@code null}; {@code null} elements are allowed. 997 * @throws IllegalArgumentException if the {@code basicSet} size is smaller 998 * than one. 999 */ 1000 public static <T> InvertibleCodec<ISeq<T>, BitGene> 1001 ofSubSet(final ISeq<? extends T> basicSet) { 1002 requireNonNull(basicSet); 1003 Requires.positive(basicSet.length()); 1004 1005 return InvertibleCodec.of( 1006 Genotype.of(BitChromosome.of(basicSet.length())), 1007 gt -> gt.chromosome() 1008 .as(BitChromosome.class).ones() 1009 .<T>mapToObj(basicSet) 1010 .collect(ISeq.toISeq()), 1011 values -> { 1012 final byte[] bits = Bits.newArray(basicSet.size()); 1013 1014 int i = 0; 1015 for (T v : values) { 1016 while (i < basicSet.size() && !Objects.equals(basicSet.get(i), v)) { 1017 ++i; 1018 } 1019 if (i >= basicSet.size()) { 1020 throw new IllegalArgumentException( 1021 "Invalid subset; input values were not created by " + 1022 "'decode' method." 1023 ); 1024 } 1025 1026 Bits.set(bits, i); 1027 } 1028 1029 return Genotype.of(new BitChromosome(bits, 0, basicSet.size())); 1030 } 1031 ); 1032 } 1033 1034 /** 1035 * The subset {@link InvertibleCodec} can be used for problems where it is 1036 * required to find the best <b>fixed-size</b> subset from a given basic set. 1037 * 1038 * @since 3.4 1039 * 1040 * @see PermutationChromosome 1041 * @see PermutationChromosome#of(ISeq, int) 1042 * 1043 * @param <T> the element type of the basic set 1044 * @param basicSet the basic set, from where to choose the <i>optimal</i> 1045 * subset. 1046 * @param size the length of the desired subsets 1047 * @return a new codec which can be used for modelling <i>subset</i> 1048 * problems. 1049 * @throws NullPointerException if the given {@code basicSet} is 1050 * {@code null}; {@code null} elements are allowed. 1051 * @throws IllegalArgumentException if {@code basicSet.size() < size}, 1052 * {@code size <= 0} or {@code basicSet.size()*size} will cause an 1053 * integer overflow. 1054 */ 1055 public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> ofSubSet( 1056 final ISeq<? extends T> basicSet, 1057 final int size 1058 ) { 1059 requireNonNull(basicSet); 1060 1061 final Map<T, EnumGene<T>> genes = 1062 IntStream.range(0, basicSet.length()) 1063 .mapToObj(i -> EnumGene.<T>of(i, basicSet)) 1064 .collect(Collectors.toMap(EnumGene::allele, identity())); 1065 1066 return InvertibleCodec.of( 1067 Genotype.of(PermutationChromosome.of(basicSet, size)), 1068 gt -> gt.chromosome().stream() 1069 .map(EnumGene::allele) 1070 .collect(ISeq.toISeq()), 1071 values -> { 1072 if (values.size() != size) { 1073 throw new IllegalArgumentException(format( 1074 "Expected sub-set size of %d, but got %d,", 1075 size, values.size() 1076 )); 1077 } 1078 1079 return Genotype.of( 1080 new PermutationChromosome<>( 1081 values.stream() 1082 .map(genes::get) 1083 .collect(ISeq.toISeq()) 1084 ) 1085 ); 1086 } 1087 ); 1088 } 1089 1090// /** 1091// * Creates a codec for a 2-dimensional affine transformation. The composed 1092// * order of the transformation is: <i>R•Sc•Sh•T</i> 1093// * 1094// * @param sx the scale factor range in x direction 1095// * @param sy the scale factor range in y direction 1096// * @param tx the translation range in x direction 1097// * @param ty the translation range in y direction 1098// * @param th the rotation range (in radians) 1099// * @param kx the shear range in x direction 1100// * @param ky the shear range in x direction 1101// * @return the affine transformation codec 1102// * @throws NullPointerException if one of the arguments is {@code null} 1103// */ 1104// static Codec<AffineTransform, DoubleGene> ofAffineTransform( 1105// final DoubleRange sx, final DoubleRange sy, 1106// final DoubleRange tx, final DoubleRange ty, 1107// final DoubleRange th, 1108// final DoubleRange kx, final DoubleRange ky 1109// ) { 1110// return Codec.of( 1111// Genotype.of( 1112// // Scale 1113// DoubleChromosome.of(sx), DoubleChromosome.of(sy), 1114// // Translation 1115// DoubleChromosome.of(tx), DoubleChromosome.of(ty), 1116// // Rotation 1117// DoubleChromosome.of(th), 1118// // Shear 1119// DoubleChromosome.of(kx), DoubleChromosome.of(ky) 1120// ), 1121// gt -> { 1122// final AffineTransform at = new AffineTransform(); 1123// 1124// at.translate( 1125// gt.getChromosome(2).getGene().doubleValue(), 1126// gt.getChromosome(3).getGene().doubleValue() 1127// ); 1128// at.shear( 1129// gt.getChromosome(5).getGene().doubleValue(), 1130// gt.getChromosome(6).getGene().doubleValue() 1131// ); 1132// at.scale( 1133// gt.getChromosome(0).getGene().doubleValue(), 1134// gt.getChromosome(1).getGene().doubleValue() 1135// ); 1136// at.rotate(gt.getChromosome(4).getGene().doubleValue()); 1137// 1138// return at; 1139// } 1140// ); 1141// } 1142// 1143// /** 1144// * Creates a codec for a 2-dimensional affine transformation. The composed 1145// * order of the transformation is: <i>R•Sc•Sh•T</i> 1146// * 1147// * @param s the scale factor range in x and y direction 1148// * @param t the translation range in x and y direction 1149// * @param th the rotation angle range 1150// * @param k the shear range in x and y direction 1151// * @return the affine transformation codec 1152// * @throws NullPointerException if one of the arguments is {@code null} 1153// */ 1154// static Codec<AffineTransform, DoubleGene> ofAffineTransform( 1155// final DoubleRange s, 1156// final DoubleRange t, 1157// final DoubleRange th, 1158// final DoubleRange k 1159// ) { 1160// return ofAffineTransform(s, s, t, t, th, k, k); 1161// } 1162 1163}