Codecs.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-4.4.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     @SuppressWarnings("unchecked")
486     private static <T> T[] newArray(final Class<?> type, final int length) {
487         return (T[])newInstance(type, length);
488     }
489 
490     /**
491      * Return a 2-dimensional matrix {@code Codec} for the given range. All
492      * matrix values are restricted by the same domain. The dimension of the
493      * returned matrix is {@code int[rows][cols]}.
494      *
495      @since 4.4
496      *
497      @param domain the domain of the matrix values
498      @param rows the number of rows of the matrix
499      @param cols the number of columns of the matrix
500      @return a new matrix {@code Codec}
501      @throws NullPointerException if the given {@code domain} is {@code null}
502      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
503      *         smaller than one.
504      */
505     public static Codec<int[][], IntegerGene> ofMatrix(
506         final IntRange domain,
507         final int rows,
508         final int cols
509     ) {
510         requireNonNull(domain);
511         require.positive(rows);
512         require.positive(cols);
513 
514         return Codec.of(
515             Genotype.of(
516                 IntegerChromosome.of(domain, cols).instances()
517                     .limit(rows)
518                     .collect(ISeq.toISeq())
519             ),
520             gt -> gt.stream()
521                 .map(ch -> ch.stream()
522                     .mapToInt(IntegerGene::intValue)
523                     .toArray())
524                 .toArray(int[][]::new)
525         );
526     }
527 
528     /**
529      * Return a 2-dimensional matrix {@code Codec} for the given range. All
530      * matrix values are restricted by the same domain. The dimension of the
531      * returned matrix is {@code long[rows][cols]}.
532      *
533      @since 4.4
534      *
535      @param domain the domain of the matrix values
536      @param rows the number of rows of the matrix
537      @param cols the number of columns of the matrix
538      @return a new matrix {@code Codec}
539      @throws NullPointerException if the given {@code domain} is {@code null}
540      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
541      *         smaller than one.
542      */
543     public static Codec<long[][], LongGene> ofMatrix(
544         final LongRange domain,
545         final int rows,
546         final int cols
547     ) {
548         requireNonNull(domain);
549         require.positive(rows);
550         require.positive(cols);
551 
552         return Codec.of(
553             Genotype.of(
554                 LongChromosome.of(domain, cols).instances()
555                     .limit(rows)
556                     .collect(ISeq.toISeq())
557             ),
558             gt -> gt.stream()
559                 .map(ch -> ch.stream()
560                     .mapToLong(LongGene::longValue)
561                     .toArray())
562                 .toArray(long[][]::new)
563         );
564     }
565 
566     /**
567      * Return a 2-dimensional matrix {@code Codec} for the given range. All
568      * matrix values are restricted by the same domain. The dimension of the
569      * returned matrix is {@code double[rows][cols]}.
570      *
571      @since 4.4
572      *
573      @param domain the domain of the matrix values
574      @param rows the number of rows of the matrix
575      @param cols the number of columns of the matrix
576      @return a new matrix {@code Codec}
577      @throws NullPointerException if the given {@code domain} is {@code null}
578      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
579      *         smaller than one.
580      */
581     public static Codec<double[][], DoubleGene> ofMatrix(
582         final DoubleRange domain,
583         final int rows,
584         final int cols
585     ) {
586         requireNonNull(domain);
587         require.positive(rows);
588         require.positive(cols);
589 
590         return Codec.of(
591             Genotype.of(
592                 DoubleChromosome.of(domain, cols).instances()
593                     .limit(rows)
594                     .collect(ISeq.toISeq())
595             ),
596             gt -> gt.stream()
597                 .map(ch -> ch.stream()
598                     .mapToDouble(DoubleGene::doubleValue)
599                     .toArray())
600                 .toArray(double[][]::new)
601         );
602     }
603 
604     /**
605      * Create a permutation {@code Codec} with the given alleles.
606      *
607      @param alleles the alleles of the permutation
608      @param <T> the allele type
609      @return a new permutation {@code Codec}
610      @throws IllegalArgumentException if the given allele array is empty
611      @throws NullPointerException if one of the alleles is {@code null}
612      */
613     public static <T> Codec<ISeq<T>, EnumGene<T>>
614     ofPermutation(final ISeq<? extends T> alleles) {
615         if (alleles.isEmpty()) {
616             throw new IllegalArgumentException(
617                 "Empty allele array is not allowed."
618             );
619         }
620 
621         return Codec.of(
622             Genotype.of(PermutationChromosome.of(alleles)),
623             gt -> gt.getChromosome().stream()
624                 .map(EnumGene::getAllele)
625                 .collect(ISeq.toISeq())
626         );
627     }
628 
629     /**
630      * Create a codec, which creates a a mapping from the elements given in the
631      * {@code source} sequence to the elements given in the {@code target}
632      * sequence. The returned mapping can be seen as a function which maps every
633      * element of the {@code target} set to an element of the {@code source} set.
634      *
635      <pre>{@code
636      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
637      * final ISeq<String> strings = ISeq.of("1", "2", "3");
638      *
639      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
640      *     Codecs.ofMapping(numbers, strings, HashMap::new);
641      * }</pre>
642      *
643      * If {@code source.size() > target.size()}, the created mapping is
644      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
645      * if {@code source.size() < target.size()}, the mapping is
646      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
647      * and if both sets have the same size, the returned mapping is
648      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
649      *
650      @since 4.3
651      *
652      @param source the source elements. Will be the <em>keys</em> of the
653      *        encoded {@code Map}.
654      @param target the target elements. Will be the <em>values</em> of the
655      *           encoded {@code Map}.
656      @param mapSupplier a function which returns a new, empty Map into which
657      *        the mapping will be inserted
658      @param <A> the type of the source elements
659      @param <B> the type of the target elements
660      @param <M> the type of the encoded Map
661      @return a new mapping codec
662      @throws IllegalArgumentException if the {@code target} sequences are empty
663      @throws NullPointerException if one of the argument is {@code null}
664      */
665     public static <A, B, M extends Map<A, B>> Codec<M, EnumGene<Integer>>
666     ofMapping(
667         final ISeq<? extends A> source,
668         final ISeq<? extends B> target,
669         final Supplier<M> mapSupplier
670     ) {
671         requireNonNull(mapSupplier);
672         return ofPermutation(target.size())
673             .map(perm -> toMapping(perm, source, target, mapSupplier));
674     }
675 
676     private static <A, B, M extends Map<A, B>> M toMapping(
677         final int[] perm,
678         final ISeq<? extends A> source,
679         final ISeq<? extends B> target,
680         final Supplier<M> mapSupplier
681     ) {
682         return IntStream.range(0, source.size())
683             .mapToObj(i -> new SimpleImmutableEntry<>(
684                 source.get(i), target.get(perm[i%perm.length])))
685             .collect(Collectors.toMap(
686                 Entry::getKey,
687                 Entry::getValue,
688                 (u,v-> {throw new IllegalStateException("Duplicate key " + u);},
689                 mapSupplier));
690     }
691 
692     /**
693      * Create a codec, which creates a a mapping from the elements given in the
694      * {@code source} sequence to the elements given in the {@code target}
695      * sequence. The returned mapping can be seen as a function which maps every
696      * element of the {@code target} set to an element of the {@code source} set.
697      *
698      <pre>{@code
699      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
700      * final ISeq<String> strings = ISeq.of("1", "2", "3");
701      *
702      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
703      *     Codecs.ofMapping(numbers, strings);
704      * }</pre>
705      *
706      * If {@code source.size() > target.size()}, the created mapping is
707      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
708      * if {@code source.size() < target.size()}, the mapping is
709      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
710      * and if both sets have the same size, the returned mapping is
711      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
712      *
713      @since 4.3
714      *
715      @param source the source elements. Will be the <em>keys</em> of the
716      *        encoded {@code Map}.
717      @param target the target elements. Will be the <em>values</em> of the
718      *           encoded {@code Map}.
719      @param <A> the type of the source elements
720      @param <B> the type of the target elements
721      @return a new mapping codec
722      @throws IllegalArgumentException if the {@code target} sequences are empty
723      @throws NullPointerException if one of the argument is {@code null}
724      */
725     public static <A, B> Codec<Map<A, B>, EnumGene<Integer>>
726     ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
727         return ofMapping(source, target, HashMap::new);
728     }
729 
730     /**
731      * The subset {@code Codec} can be used for problems where it is required to
732      * find the best <b>variable-sized</b> subset from given basic set. A typical
733      * usage example of the returned {@code Codec} is the Knapsack problem.
734      <p>
735      * The following code snippet shows a simplified variation of the Knapsack
736      * problem.
737      <pre>{@code
738      * public final class Main {
739      *     // The basic set from where to choose an 'optimal' subset.
740      *     private final static ISeq<Integer> SET =
741      *         ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
742      *
743      *     // Fitness function directly takes an 'int' value.
744      *     private static int fitness(final ISeq<Integer> subset) {
745      *         assert(subset.size() <= SET.size());
746      *         final int size = subset.stream()
747      *             .collect(Collectors.summingInt(Integer::intValue));
748      *         return size <= 20 ? size : 0;
749      *     }
750      *
751      *     public static void main(final String[] args) {
752      *         final Engine<BitGene, Double> engine = Engine
753      *             .builder(Main::fitness, codec.ofSubSet(SET))
754      *             .build();
755      *         ...
756      *     }
757      * }
758      * }</pre>
759      *
760      @param <T> the element type of the basic set
761      @param basicSet the basic set, from where to choose the <i>optimal</i>
762      *        subset.
763      @return a new codec which can be used for modelling <i>subset</i>
764      *         problems.
765      @throws NullPointerException if the given {@code basicSet} is
766      *         {@code null}; {@code null} elements are allowed.
767      @throws IllegalArgumentException if the {@code basicSet} size is smaller
768      *         than one.
769      */
770     public static <T> Codec<ISeq<T>, BitGene> ofSubSet(
771         final ISeq<? extends T> basicSet
772     ) {
773         requireNonNull(basicSet);
774         require.positive(basicSet.length());
775 
776         return Codec.of(
777             Genotype.of(BitChromosome.of(basicSet.length())),
778             gt -> gt.getChromosome()
779                 .as(BitChromosome.class).ones()
780                 .<T>mapToObj(basicSet)
781                 .collect(ISeq.toISeq())
782         );
783     }
784 
785     /**
786      * The subset {@code Codec} can be used for problems where it is required to
787      * find the best <b>fixed-size</b> subset from given basic set.
788      *
789      @since 3.4
790      *
791      @see PermutationChromosome
792      @see PermutationChromosome#of(ISeq, int)
793      *
794      @param <T> the element type of the basic set
795      @param basicSet the basic set, from where to choose the <i>optimal</i>
796      *        subset.
797      @param size the length of the desired subsets
798      @return a new codec which can be used for modelling <i>subset</i>
799      *         problems.
800      @throws NullPointerException if the given {@code basicSet} is
801      *         {@code null}; {@code null} elements are allowed.
802      @throws IllegalArgumentException if {@code basicSet.size() < size},
803      *         {@code size <= 0} or {@code basicSet.size()*size} will cause an
804      *         integer overflow.
805      */
806     public static <T> Codec<ISeq<T>, EnumGene<T>> ofSubSet(
807         final ISeq<? extends T> basicSet,
808         final int size
809     ) {
810         requireNonNull(basicSet);
811         comb.checkSubSet(basicSet.size(), size);
812 
813         return Codec.of(
814             Genotype.of(PermutationChromosome.of(basicSet, size)),
815             gt -> gt.getChromosome().stream()
816                 .map(EnumGene::getAllele)
817                 .collect(ISeq.toISeq())
818         );
819     }
820 
821 //    /**
822 //     * Creates a codec for a 2-dimensional affine transformation. The composed
823 //     * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
824 //     *
825 //     * @param sx the scale factor range in x direction
826 //     * @param sy the scale factor range in y direction
827 //     * @param tx the translation range in x direction
828 //     * @param ty the translation range in y direction
829 //     * @param th the rotation range (in radians)
830 //     * @param kx the shear range in x direction
831 //     * @param ky the shear range in x direction
832 //     * @return the affine transformation codec
833 //     * @throws NullPointerException if one of the arguments is {@code null}
834 //     */
835 //    static Codec<AffineTransform, DoubleGene> ofAffineTransform(
836 //        final DoubleRange sx, final DoubleRange sy,
837 //        final DoubleRange tx, final DoubleRange ty,
838 //        final DoubleRange th,
839 //        final DoubleRange kx, final DoubleRange ky
840 //    ) {
841 //        return Codec.of(
842 //            Genotype.of(
843 //                // Scale
844 //                DoubleChromosome.of(sx), DoubleChromosome.of(sy),
845 //                // Translation
846 //                DoubleChromosome.of(tx), DoubleChromosome.of(ty),
847 //                // Rotation
848 //                DoubleChromosome.of(th),
849 //                // Shear
850 //                DoubleChromosome.of(kx), DoubleChromosome.of(ky)
851 //            ),
852 //            gt -> {
853 //                final AffineTransform at = new AffineTransform();
854 //
855 //                at.translate(
856 //                    gt.getChromosome(2).getGene().doubleValue(),
857 //                    gt.getChromosome(3).getGene().doubleValue()
858 //                );
859 //                at.shear(
860 //                    gt.getChromosome(5).getGene().doubleValue(),
861 //                    gt.getChromosome(6).getGene().doubleValue()
862 //                );
863 //                at.scale(
864 //                    gt.getChromosome(0).getGene().doubleValue(),
865 //                    gt.getChromosome(1).getGene().doubleValue()
866 //                );
867 //                at.rotate(gt.getChromosome(4).getGene().doubleValue());
868 //
869 //                return at;
870 //            }
871 //        );
872 //    }
873 //
874 //    /**
875 //     * Creates a codec for a 2-dimensional affine transformation. The composed
876 //     * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
877 //     *
878 //     * @param s the scale factor range in x and y direction
879 //     * @param t the translation range in x and y direction
880 //     * @param th the rotation angle range
881 //     * @param k the shear range in x and y direction
882 //     * @return the affine transformation codec
883 //     * @throws NullPointerException if one of the arguments is {@code null}
884 //     */
885 //    static Codec<AffineTransform, DoubleGene> ofAffineTransform(
886 //        final DoubleRange s,
887 //        final DoubleRange t,
888 //        final DoubleRange th,
889 //        final DoubleRange k
890 //    ) {
891 //        return ofAffineTransform(s, s, t, t, th, k, k);
892 //    }
893 
894 }