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