Codecs.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-5.0.0).
003  * Copyright (c) 2007-2019 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  */
020 package io.jenetics.engine;
021 
022 import static java.lang.reflect.Array.newInstance;
023 import static java.util.Objects.requireNonNull;
024 
025 import java.util.AbstractMap.SimpleImmutableEntry;
026 import java.util.HashMap;
027 import java.util.Map;
028 import java.util.Map.Entry;
029 import java.util.Objects;
030 import java.util.function.Predicate;
031 import java.util.function.Supplier;
032 import java.util.stream.Collectors;
033 import java.util.stream.IntStream;
034 import java.util.stream.Stream;
035 
036 import io.jenetics.AnyChromosome;
037 import io.jenetics.AnyGene;
038 import io.jenetics.BitChromosome;
039 import io.jenetics.BitGene;
040 import io.jenetics.DoubleChromosome;
041 import io.jenetics.DoubleGene;
042 import io.jenetics.EnumGene;
043 import io.jenetics.Gene;
044 import io.jenetics.Genotype;
045 import io.jenetics.IntegerChromosome;
046 import io.jenetics.IntegerGene;
047 import io.jenetics.LongChromosome;
048 import io.jenetics.LongGene;
049 import io.jenetics.PermutationChromosome;
050 import io.jenetics.internal.math.comb;
051 import io.jenetics.internal.util.Predicates;
052 import io.jenetics.internal.util.require;
053 import io.jenetics.util.DoubleRange;
054 import io.jenetics.util.ISeq;
055 import io.jenetics.util.IntRange;
056 import io.jenetics.util.LongRange;
057 
058 /**
059  * This class contains factory methods for creating common  problem encodings.
060  *
061  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
062  @since 3.2
063  @version 4.4
064  */
065 public final class Codecs {
066 
067     private Codecs() {}
068 
069     /**
070      * Return a scalar {@code Codec} for the given range.
071      *
072      @param domain the domain of the returned {@code Codec}
073      @return a new scalar {@code Codec} with the given domain.
074      @throws NullPointerException if the given {@code domain} is {@code null}
075      */
076     public static Codec<Integer, IntegerGene> ofScalar(final IntRange domain) {
077         requireNonNull(domain);
078 
079         return Codec.of(
080             Genotype.of(IntegerChromosome.of(domain)),
081             gt -> gt.getChromosome().getGene().getAllele()
082         );
083     }
084 
085     /**
086      * Return a scalar {@code Codec} for the given range.
087      *
088      @param domain the domain of the returned {@code Codec}
089      @return a new scalar {@code Codec} with the given domain.
090      @throws NullPointerException if the given {@code domain} is {@code null}
091      */
092     public static Codec<Long, LongGene> ofScalar(final LongRange domain) {
093         requireNonNull(domain);
094 
095         return Codec.of(
096             Genotype.of(LongChromosome.of(domain)),
097             gt -> gt.getGene().getAllele()
098         );
099     }
100 
101     /**
102      * Return a scalar {@code Codec} for the given range.
103      *
104      @param domain the domain of the returned {@code Codec}
105      @return a new scalar {@code Codec} with the given domain.
106      @throws NullPointerException if the given {@code domain} is {@code null}
107      */
108     public static Codec<Double, DoubleGene> ofScalar(final DoubleRange domain) {
109         requireNonNull(domain);
110 
111         return Codec.of(
112             Genotype.of(DoubleChromosome.of(domain)),
113             gt -> gt.getGene().getAllele()
114         );
115     }
116 
117     /**
118      * Return a scala {@code Codec} with the given allele {@link Supplier} and
119      * allele {@code validator}. The {@code supplier} is responsible for
120      * creating new random alleles, and the {@code validator} can verify it.
121      <p>
122      * The following example shows a codec which creates and verifies
123      * {@code BigInteger} objects.
124      <pre>{@code
125      * final Codec<BigInteger, AnyGene<BigInteger>> codec = Codecs.of(
126      *     // Create new random 'BigInteger' object.
127      *     () -> {
128      *         final byte[] data = new byte[100];
129      *         RandomRegistry.getRandom().nextBytes(data);
130      *         return new BigInteger(data);
131      *     },
132      *     // Verify that bit 7 is set. (For illustration purpose.)
133      *     bi -> bi.testBit(7)
134      * );
135      * }</pre>
136      *
137      @see AnyGene#of(Supplier, Predicate)
138      @see AnyChromosome#of(Supplier, Predicate)
139      *
140      @param <A> the allele type
141      @param supplier the allele-supplier which is used for creating new,
142      *        random alleles
143      @param validator the validator used for validating the created gene. This
144      *        predicate is used in the {@link AnyGene#isValid()} method.
145      @return a new {@code Codec} with the given parameters
146      @throws NullPointerException if one of the parameters is {@code null}
147      */
148     public static <A> Codec<A, AnyGene<A>> ofScalar(
149         final Supplier<? extends A> supplier,
150         final Predicate<? super A> validator
151     ) {
152         return Codec.of(
153             Genotype.of(AnyChromosome.of(supplier, validator)),
154             gt -> gt.getGene().getAllele()
155         );
156     }
157 
158     /**
159      * Return a scala {@code Codec} with the given allele {@link Supplier} and
160      * allele {@code validator}. The {@code supplier} is responsible for
161      * creating new random alleles.
162      *
163      @see #ofScalar(Supplier, Predicate)
164      @see AnyGene#of(Supplier)
165      @see AnyChromosome#of(Supplier)
166      *
167      @param <A> the allele type
168      @param supplier the allele-supplier which is used for creating new,
169      *        random alleles
170      @return a new {@code Codec} with the given parameters
171      @throws NullPointerException if the parameter is {@code null}
172      */
173     public static <A> Codec<A, AnyGene<A>> ofScalar(
174         final Supplier<? extends A> supplier
175     ) {
176         return Codec.of(
177             Genotype.of(AnyChromosome.of(supplier)),
178             gt -> gt.getGene().getAllele()
179         );
180     }
181 
182     /**
183      * Return a vector {@code Codec} for the given range. All vector values
184      * are restricted by the same domain.
185      *
186      @param domain the domain of the vector values
187      @param length the vector length
188      @return a new vector {@code Codec}
189      @throws NullPointerException if the given {@code domain} is {@code null}
190      @throws IllegalArgumentException if the {@code length} is smaller than
191      *         one.
192      */
193     public static Codec<int[], IntegerGene> ofVector(
194         final IntRange domain,
195         final int length
196     ) {
197         requireNonNull(domain);
198         require.positive(length);
199 
200         return Codec.of(
201             Genotype.of(IntegerChromosome.of(domain, length)),
202             gt -> gt.getChromosome().as(IntegerChromosome.class).toArray()
203         );
204     }
205 
206     /**
207      * Return a vector {@code Codec} for the given range. All vector values
208      * are restricted by the same domain.
209      *
210      @param domain the domain of the vector values
211      @param length the vector length
212      @return a new vector {@code Codec}
213      @throws NullPointerException if the given {@code domain} is {@code null}
214      @throws IllegalArgumentException if the {@code length} is smaller than
215      *         one.
216      */
217     public static Codec<long[], LongGene> ofVector(
218         final LongRange domain,
219         final int length
220     ) {
221         requireNonNull(domain);
222         require.positive(length);
223 
224         return Codec.of(
225             Genotype.of(LongChromosome.of(domain, length)),
226             gt -> gt.getChromosome().as(LongChromosome.class).toArray()
227         );
228     }
229 
230     /**
231      * Return a vector {@code Codec} for the given range. All vector values
232      * are restricted by the same domain.
233      *
234      @param domain the domain of the vector values
235      @param length the vector length
236      @return a new vector {@code Codec}
237      @throws NullPointerException if the given {@code domain} is {@code null}
238      @throws IllegalArgumentException if the {@code length} is smaller than
239      *         one.
240      */
241     public static Codec<double[], DoubleGene> ofVector(
242         final DoubleRange domain,
243         final int length
244     ) {
245         requireNonNull(domain);
246         require.positive(length);
247 
248         return Codec.of(
249             Genotype.of(DoubleChromosome.of(domain, length)),
250             gt -> gt.getChromosome().as(DoubleChromosome.class).toArray()
251         );
252     }
253 
254     /**
255      * Create a vector {@code Codec} for the given ranges. Each vector element
256      * might have a different domain. The vector length is equal to the number
257      * of domains.
258      *
259      @param domains the domain ranges
260      @return a new vector {@code Codec}
261      @throws NullPointerException if one of the arguments is {@code null}
262      @throws IllegalArgumentException if the {@code domains} array is empty
263      */
264     public static Codec<int[], IntegerGene> ofVector(final IntRange... domains) {
265         if (domains.length == 0) {
266             throw new IllegalArgumentException("Domains must not be empty.");
267         }
268 
269         final ISeq<IntegerChromosome> chromosomes = Stream.of(domains)
270             .peek(Objects::requireNonNull)
271             .map(IntegerGene::of)
272             .map(IntegerChromosome::of)
273             .collect(ISeq.toISeq());
274 
275         return Codec.of(
276             Genotype.of(chromosomes),
277             gt -> {
278                 final int[] args = new int[gt.length()];
279                 for (int i = gt.length(); --i >= 0;) {
280                     args[i= gt.getChromosome(i).getGene().intValue();
281                 }
282                 return args;
283             }
284         );
285     }
286 
287     /**
288      * Create a vector {@code Codec} for the given ranges. Each vector element
289      * might have a different domain. The vector length is equal to the number
290      * of domains.
291      *
292      @param domains the domain ranges
293      @return a new vector {@code Codec}
294      @throws NullPointerException if one of the arguments is {@code null}
295      @throws IllegalArgumentException if the {@code domains} array is empty
296      */
297     public static Codec<long[], LongGene> ofVector(final LongRange... domains) {
298         if (domains.length == 0) {
299             throw new IllegalArgumentException("Domains must not be empty.");
300         }
301 
302         final ISeq<LongChromosome> chromosomes = Stream.of(domains)
303             .peek(Objects::requireNonNull)
304             .map(LongGene::of)
305             .map(LongChromosome::of)
306             .collect(ISeq.toISeq());
307 
308         return Codec.of(
309             Genotype.of(chromosomes),
310             gt -> {
311                 final long[] args = new long[gt.length()];
312                 for (int i = gt.length(); --i >= 0;) {
313                     args[i= gt.getChromosome(i).getGene().longValue();
314                 }
315                 return args;
316             }
317         );
318     }
319 
320     /**
321      * Create a vector {@code Codec} for the given ranges. Each vector element
322      * might have a different domain. The vector length is equal to the number
323      * of domains.
324      *
325      @param domains the domain ranges
326      @return a new vector {@code Codec}
327      @throws NullPointerException if one of the arguments is {@code null}
328      @throws IllegalArgumentException if the {@code domains} array is empty
329      */
330     public static Codec<double[], DoubleGene> ofVector(
331         final DoubleRange... domains
332     ) {
333         if (domains.length == 0) {
334             throw new IllegalArgumentException("Domains must not be empty.");
335         }
336 
337         final ISeq<DoubleChromosome> chromosomes = Stream.of(domains)
338             .peek(Objects::requireNonNull)
339             .map(DoubleGene::of)
340             .map(DoubleChromosome::of)
341             .collect(ISeq.toISeq());
342 
343         return Codec.of(
344             Genotype.of(chromosomes),
345             gt -> {
346                 final double[] args = new double[gt.length()];
347                 for (int i = gt.length(); --i >= 0;) {
348                     args[i= gt.getChromosome(i).getGene().doubleValue();
349                 }
350                 return args;
351             }
352         );
353     }
354 
355     /**
356      * Return a scala {@code Codec} with the given allele {@link Supplier},
357      * allele {@code validator} and {@code Chromosome} length. The
358      * {@code supplier} is responsible for creating new random alleles, and the
359      * {@code validator} can verify it.
360      <p>
361      * The following example shows a codec which creates and verifies
362      * {@code BigInteger} object arrays.
363      <pre>{@code
364      * final Codec<BigInteger[], AnyGene<BigInteger>> codec = Codecs.of(
365      *     // Create new random 'BigInteger' object.
366      *     () -> {
367      *         final byte[] data = new byte[100];
368      *         RandomRegistry.getRandom().nextBytes(data);
369      *         return new BigInteger(data);
370      *     },
371      *     // Verify that bit 7 is set. (For illustration purpose.)
372      *     bi -> bi.testBit(7),
373      *     // The 'Chromosome' length.
374      *     123
375      * );
376      * }</pre>
377      *
378      @see AnyChromosome#of(Supplier, Predicate, Predicate, int)
379      *
380      @param <A> the allele type
381      @param supplier the allele-supplier which is used for creating new,
382      *        random alleles
383      @param alleleValidator the validator used for validating the created gene.
384      *        This predicate is used in the {@link AnyGene#isValid()} method.
385      @param alleleSeqValidator the validator used for validating the created
386      *        chromosome. This predicate is used in the
387      *        {@link AnyChromosome#isValid()} method.
388      @param length the vector length
389      @return a new {@code Codec} with the given parameters
390      @throws NullPointerException if one of the parameters is {@code null}
391      @throws IllegalArgumentException if the length of the vector is smaller
392      *         than one.
393      */
394     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
395         final Supplier<? extends A> supplier,
396         final Predicate<? super A> alleleValidator,
397         final Predicate<? super ISeq<A>> alleleSeqValidator,
398         final int length
399     ) {
400         requireNonNull(supplier);
401         requireNonNull(alleleSeqValidator);
402         requireNonNull(alleleSeqValidator);
403         require.positive(length);
404 
405         return Codec.of(
406             Genotype.of(AnyChromosome
407                 .of(supplier, alleleValidator, alleleSeqValidator, length)),
408             gt -> gt.getChromosome().stream()
409                 .map(Gene::getAllele)
410                 .collect(ISeq.toISeq())
411         );
412     }
413 
414     /**
415      * Return a scala {@code Codec} with the given allele {@link Supplier},
416      * allele {@code validator} and {@code Chromosome} length. The
417      * {@code supplier} is responsible for creating new random alleles, and the
418      * {@code validator} can verify it.
419      *
420      @param <A> the allele type
421      @param supplier the allele-supplier which is used for creating new,
422      *        random alleles
423      @param validator the validator used for validating the created gene. This
424      *        predicate is used in the {@link AnyGene#isValid()} method.
425      @param length the vector length
426      @return a new {@code Codec} with the given parameters
427      @throws NullPointerException if one of the parameters is {@code null}
428      @throws IllegalArgumentException if the length of the vector is smaller
429      *         than one.
430      */
431     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
432         final Supplier<? extends A> supplier,
433         final Predicate<? super A> validator,
434         final int length
435     ) {
436         return ofVector(
437             supplier,
438             validator,
439             Predicates.<ISeq<A>>True(),
440             length
441         );
442     }
443 
444     /**
445      * Return a scala {@code Codec} with the given allele {@link Supplier} and
446      * {@code Chromosome} length. The {@code supplier} is responsible for
447      * creating new random alleles.
448      *
449      @param <A> the allele type
450      @param supplier the allele-supplier which is used for creating new,
451      *        random alleles
452      @param length the vector length
453      @return a new {@code Codec} with the given parameters
454      @throws NullPointerException if one of the parameters is {@code null}
455      @throws IllegalArgumentException if the length of the vector is smaller
456      *         than one.
457      */
458     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
459         final Supplier<? extends A> supplier,
460         final int length
461     ) {
462         return ofVector(supplier, Predicates.TRUE, length);
463     }
464 
465     /**
466      * Create a permutation {@code Codec} of integer in the range
467      * {@code [0, length)}.
468      *
469      @param length the number of permutation elements
470      @return a permutation {@code Codec} of integers
471      @throws IllegalArgumentException if the {@code length} is smaller than
472      *         one.
473      */
474     public static Codec<int[], EnumGene<Integer>> ofPermutation(final int length) {
475         require.positive(length);
476 
477         return Codec.of(
478             Genotype.of(PermutationChromosome.ofInteger(length)),
479             gt -> gt.getChromosome().stream()
480                 .mapToInt(EnumGene::getAllele)
481                 .toArray()
482         );
483     }
484 
485     /**
486      * Return a 2-dimensional matrix {@code Codec} for the given range. All
487      * matrix values are restricted by the same domain. The dimension of the
488      * returned matrix is {@code int[rows][cols]}.
489      *
490      @since 4.4
491      *
492      @param domain the domain of the matrix values
493      @param rows the number of rows of the matrix
494      @param cols the number of columns of the matrix
495      @return a new matrix {@code Codec}
496      @throws NullPointerException if the given {@code domain} is {@code null}
497      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
498      *         smaller than one.
499      */
500     public static Codec<int[][], IntegerGene> ofMatrix(
501         final IntRange domain,
502         final int rows,
503         final int cols
504     ) {
505         requireNonNull(domain);
506         require.positive(rows);
507         require.positive(cols);
508 
509         return Codec.of(
510             Genotype.of(
511                 IntegerChromosome.of(domain, cols).instances()
512                     .limit(rows)
513                     .collect(ISeq.toISeq())
514             ),
515             gt -> gt.stream()
516                 .map(ch -> ch.stream()
517                     .mapToInt(IntegerGene::intValue)
518                     .toArray())
519                 .toArray(int[][]::new)
520         );
521     }
522 
523     /**
524      * Return a 2-dimensional matrix {@code Codec} for the given range. All
525      * matrix values are restricted by the same domain. The dimension of the
526      * returned matrix is {@code long[rows][cols]}.
527      *
528      @since 4.4
529      *
530      @param domain the domain of the matrix values
531      @param rows the number of rows of the matrix
532      @param cols the number of columns of the matrix
533      @return a new matrix {@code Codec}
534      @throws NullPointerException if the given {@code domain} is {@code null}
535      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
536      *         smaller than one.
537      */
538     public static Codec<long[][], LongGene> ofMatrix(
539         final LongRange domain,
540         final int rows,
541         final int cols
542     ) {
543         requireNonNull(domain);
544         require.positive(rows);
545         require.positive(cols);
546 
547         return Codec.of(
548             Genotype.of(
549                 LongChromosome.of(domain, cols).instances()
550                     .limit(rows)
551                     .collect(ISeq.toISeq())
552             ),
553             gt -> gt.stream()
554                 .map(ch -> ch.stream()
555                     .mapToLong(LongGene::longValue)
556                     .toArray())
557                 .toArray(long[][]::new)
558         );
559     }
560 
561     /**
562      * Return a 2-dimensional matrix {@code Codec} for the given range. All
563      * matrix values are restricted by the same domain. The dimension of the
564      * returned matrix is {@code double[rows][cols]}.
565      *
566      @since 4.4
567      *
568      @param domain the domain of the matrix values
569      @param rows the number of rows of the matrix
570      @param cols the number of columns of the matrix
571      @return a new matrix {@code Codec}
572      @throws NullPointerException if the given {@code domain} is {@code null}
573      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
574      *         smaller than one.
575      */
576     public static Codec<double[][], DoubleGene> ofMatrix(
577         final DoubleRange domain,
578         final int rows,
579         final int cols
580     ) {
581         requireNonNull(domain);
582         require.positive(rows);
583         require.positive(cols);
584 
585         return Codec.of(
586             Genotype.of(
587                 DoubleChromosome.of(domain, cols).instances()
588                     .limit(rows)
589                     .collect(ISeq.toISeq())
590             ),
591             gt -> gt.stream()
592                 .map(ch -> ch.stream()
593                     .mapToDouble(DoubleGene::doubleValue)
594                     .toArray())
595                 .toArray(double[][]::new)
596         );
597     }
598 
599     /**
600      * Create a permutation {@code Codec} with the given alleles.
601      *
602      @param alleles the alleles of the permutation
603      @param <T> the allele type
604      @return a new permutation {@code Codec}
605      @throws IllegalArgumentException if the given allele array is empty
606      @throws NullPointerException if one of the alleles is {@code null}
607      */
608     public static <T> Codec<ISeq<T>, EnumGene<T>>
609     ofPermutation(final ISeq<? extends T> alleles) {
610         if (alleles.isEmpty()) {
611             throw new IllegalArgumentException(
612                 "Empty allele array is not allowed."
613             );
614         }
615 
616         return Codec.of(
617             Genotype.of(PermutationChromosome.of(alleles)),
618             gt -> gt.getChromosome().stream()
619                 .map(EnumGene::getAllele)
620                 .collect(ISeq.toISeq())
621         );
622     }
623 
624     /**
625      * Create a codec, which creates a a mapping from the elements given in the
626      * {@code source} sequence to the elements given in the {@code target}
627      * sequence. The returned mapping can be seen as a function which maps every
628      * element of the {@code target} set to an element of the {@code source} set.
629      *
630      <pre>{@code
631      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
632      * final ISeq<String> strings = ISeq.of("1", "2", "3");
633      *
634      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
635      *     Codecs.ofMapping(numbers, strings, HashMap::new);
636      * }</pre>
637      *
638      * If {@code source.size() > target.size()}, the created mapping is
639      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
640      * if {@code source.size() < target.size()}, the mapping is
641      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
642      * and if both sets have the same size, the returned mapping is
643      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
644      *
645      @since 4.3
646      *
647      @param source the source elements. Will be the <em>keys</em> of the
648      *        encoded {@code Map}.
649      @param target the target elements. Will be the <em>values</em> of the
650      *           encoded {@code Map}.
651      @param mapSupplier a function which returns a new, empty Map into which
652      *        the mapping will be inserted
653      @param <A> the type of the source elements
654      @param <B> the type of the target elements
655      @param <M> the type of the encoded Map
656      @return a new mapping codec
657      @throws IllegalArgumentException if the {@code target} sequences are empty
658      @throws NullPointerException if one of the argument is {@code null}
659      */
660     public static <A, B, M extends Map<A, B>> Codec<M, EnumGene<Integer>>
661     ofMapping(
662         final ISeq<? extends A> source,
663         final ISeq<? extends B> target,
664         final Supplier<M> mapSupplier
665     ) {
666         requireNonNull(mapSupplier);
667         return ofPermutation(target.size())
668             .map(perm -> toMapping(perm, source, target, mapSupplier));
669     }
670 
671     private static <A, B, M extends Map<A, B>> M toMapping(
672         final int[] perm,
673         final ISeq<? extends A> source,
674         final ISeq<? extends B> target,
675         final Supplier<M> mapSupplier
676     ) {
677         return IntStream.range(0, source.size())
678             .mapToObj(i -> new SimpleImmutableEntry<>(
679                 source.get(i), target.get(perm[i%perm.length])))
680             .collect(Collectors.toMap(
681                 Entry::getKey,
682                 Entry::getValue,
683                 (u,v-> {throw new IllegalStateException("Duplicate key " + u);},
684                 mapSupplier));
685     }
686 
687     /**
688      * Create a codec, which creates a a mapping from the elements given in the
689      * {@code source} sequence to the elements given in the {@code target}
690      * sequence. The returned mapping can be seen as a function which maps every
691      * element of the {@code target} set to an element of the {@code source} set.
692      *
693      <pre>{@code
694      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
695      * final ISeq<String> strings = ISeq.of("1", "2", "3");
696      *
697      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
698      *     Codecs.ofMapping(numbers, strings);
699      * }</pre>
700      *
701      * If {@code source.size() > target.size()}, the created mapping is
702      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
703      * if {@code source.size() < target.size()}, the mapping is
704      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
705      * and if both sets have the same size, the returned mapping is
706      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
707      *
708      @since 4.3
709      *
710      @param source the source elements. Will be the <em>keys</em> of the
711      *        encoded {@code Map}.
712      @param target the target elements. Will be the <em>values</em> of the
713      *           encoded {@code Map}.
714      @param <A> the type of the source elements
715      @param <B> the type of the target elements
716      @return a new mapping codec
717      @throws IllegalArgumentException if the {@code target} sequences are empty
718      @throws NullPointerException if one of the argument is {@code null}
719      */
720     public static <A, B> Codec<Map<A, B>, EnumGene<Integer>>
721     ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
722         return ofMapping(source, target, HashMap::new);
723     }
724 
725     /**
726      * The subset {@code Codec} can be used for problems where it is required to
727      * find the best <b>variable-sized</b> subset from given basic set. A typical
728      * usage example of the returned {@code Codec} is the Knapsack problem.
729      <p>
730      * The following code snippet shows a simplified variation of the Knapsack
731      * problem.
732      <pre>{@code
733      * public final class Main {
734      *     // The basic set from where to choose an 'optimal' subset.
735      *     private final static ISeq<Integer> SET =
736      *         ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
737      *
738      *     // Fitness function directly takes an 'int' value.
739      *     private static int fitness(final ISeq<Integer> subset) {
740      *         assert(subset.size() <= SET.size());
741      *         final int size = subset.stream()
742      *             .collect(Collectors.summingInt(Integer::intValue));
743      *         return size <= 20 ? size : 0;
744      *     }
745      *
746      *     public static void main(final String[] args) {
747      *         final Engine<BitGene, Double> engine = Engine
748      *             .builder(Main::fitness, codec.ofSubSet(SET))
749      *             .build();
750      *         ...
751      *     }
752      * }
753      * }</pre>
754      *
755      @param <T> the element type of the basic set
756      @param basicSet the basic set, from where to choose the <i>optimal</i>
757      *        subset.
758      @return a new codec which can be used for modelling <i>subset</i>
759      *         problems.
760      @throws NullPointerException if the given {@code basicSet} is
761      *         {@code null}; {@code null} elements are allowed.
762      @throws IllegalArgumentException if the {@code basicSet} size is smaller
763      *         than one.
764      */
765     public static <T> Codec<ISeq<T>, BitGene> ofSubSet(
766         final ISeq<? extends T> basicSet
767     ) {
768         requireNonNull(basicSet);
769         require.positive(basicSet.length());
770 
771         return Codec.of(
772             Genotype.of(BitChromosome.of(basicSet.length())),
773             gt -> gt.getChromosome()
774                 .as(BitChromosome.class).ones()
775                 .<T>mapToObj(basicSet)
776                 .collect(ISeq.toISeq())
777         );
778     }
779 
780     /**
781      * The subset {@code Codec} can be used for problems where it is required to
782      * find the best <b>fixed-size</b> subset from given basic set.
783      *
784      @since 3.4
785      *
786      @see PermutationChromosome
787      @see PermutationChromosome#of(ISeq, int)
788      *
789      @param <T> the element type of the basic set
790      @param basicSet the basic set, from where to choose the <i>optimal</i>
791      *        subset.
792      @param size the length of the desired subsets
793      @return a new codec which can be used for modelling <i>subset</i>
794      *         problems.
795      @throws NullPointerException if the given {@code basicSet} is
796      *         {@code null}; {@code null} elements are allowed.
797      @throws IllegalArgumentException if {@code basicSet.size() < size},
798      *         {@code size <= 0} or {@code basicSet.size()*size} will cause an
799      *         integer overflow.
800      */
801     public static <T> Codec<ISeq<T>, EnumGene<T>> ofSubSet(
802         final ISeq<? extends T> basicSet,
803         final int size
804     ) {
805         requireNonNull(basicSet);
806         comb.checkSubSet(basicSet.size(), size);
807 
808         return Codec.of(
809             Genotype.of(PermutationChromosome.of(basicSet, size)),
810             gt -> gt.getChromosome().stream()
811                 .map(EnumGene::getAllele)
812                 .collect(ISeq.toISeq())
813         );
814     }
815 
816 //    /**
817 //     * Creates a codec for a 2-dimensional affine transformation. The composed
818 //     * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
819 //     *
820 //     * @param sx the scale factor range in x direction
821 //     * @param sy the scale factor range in y direction
822 //     * @param tx the translation range in x direction
823 //     * @param ty the translation range in y direction
824 //     * @param th the rotation range (in radians)
825 //     * @param kx the shear range in x direction
826 //     * @param ky the shear range in x direction
827 //     * @return the affine transformation codec
828 //     * @throws NullPointerException if one of the arguments is {@code null}
829 //     */
830 //    static Codec<AffineTransform, DoubleGene> ofAffineTransform(
831 //        final DoubleRange sx, final DoubleRange sy,
832 //        final DoubleRange tx, final DoubleRange ty,
833 //        final DoubleRange th,
834 //        final DoubleRange kx, final DoubleRange ky
835 //    ) {
836 //        return Codec.of(
837 //            Genotype.of(
838 //                // Scale
839 //                DoubleChromosome.of(sx), DoubleChromosome.of(sy),
840 //                // Translation
841 //                DoubleChromosome.of(tx), DoubleChromosome.of(ty),
842 //                // Rotation
843 //                DoubleChromosome.of(th),
844 //                // Shear
845 //                DoubleChromosome.of(kx), DoubleChromosome.of(ky)
846 //            ),
847 //            gt -> {
848 //                final AffineTransform at = new AffineTransform();
849 //
850 //                at.translate(
851 //                    gt.getChromosome(2).getGene().doubleValue(),
852 //                    gt.getChromosome(3).getGene().doubleValue()
853 //                );
854 //                at.shear(
855 //                    gt.getChromosome(5).getGene().doubleValue(),
856 //                    gt.getChromosome(6).getGene().doubleValue()
857 //                );
858 //                at.scale(
859 //                    gt.getChromosome(0).getGene().doubleValue(),
860 //                    gt.getChromosome(1).getGene().doubleValue()
861 //                );
862 //                at.rotate(gt.getChromosome(4).getGene().doubleValue());
863 //
864 //                return at;
865 //            }
866 //        );
867 //    }
868 //
869 //    /**
870 //     * Creates a codec for a 2-dimensional affine transformation. The composed
871 //     * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
872 //     *
873 //     * @param s the scale factor range in x and y direction
874 //     * @param t the translation range in x and y direction
875 //     * @param th the rotation angle range
876 //     * @param k the shear range in x and y direction
877 //     * @return the affine transformation codec
878 //     * @throws NullPointerException if one of the arguments is {@code null}
879 //     */
880 //    static Codec<AffineTransform, DoubleGene> ofAffineTransform(
881 //        final DoubleRange s,
882 //        final DoubleRange t,
883 //        final DoubleRange th,
884 //        final DoubleRange k
885 //    ) {
886 //        return ofAffineTransform(s, s, t, t, th, k, k);
887 //    }
888 
889 }