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