Codecs.java
0001 /*
0002  * Java Genetic Algorithm Library (jenetics-6.2.0).
0003  * Copyright (c) 2007-2021 Franz Wilhelmstötter
0004  *
0005  * Licensed under the Apache License, Version 2.0 (the "License");
0006  * you may not use this file except in compliance with the License.
0007  * You may obtain a copy of the License at
0008  *
0009  *      http://www.apache.org/licenses/LICENSE-2.0
0010  *
0011  * Unless required by applicable law or agreed to in writing, software
0012  * distributed under the License is distributed on an "AS IS" BASIS,
0013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014  * See the License for the specific language governing permissions and
0015  * limitations under the License.
0016  *
0017  * Author:
0018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
0019  */
0020 package io.jenetics.engine;
0021 
0022 import static java.lang.String.format;
0023 import static java.util.Objects.requireNonNull;
0024 import static java.util.function.Function.identity;
0025 
0026 import java.util.AbstractMap.SimpleImmutableEntry;
0027 import java.util.HashMap;
0028 import java.util.HashSet;
0029 import java.util.Iterator;
0030 import java.util.Map;
0031 import java.util.Map.Entry;
0032 import java.util.Objects;
0033 import java.util.Set;
0034 import java.util.function.Predicate;
0035 import java.util.function.Supplier;
0036 import java.util.stream.Collectors;
0037 import java.util.stream.DoubleStream;
0038 import java.util.stream.IntStream;
0039 import java.util.stream.LongStream;
0040 import java.util.stream.Stream;
0041 
0042 import io.jenetics.AnyChromosome;
0043 import io.jenetics.AnyGene;
0044 import io.jenetics.BitChromosome;
0045 import io.jenetics.BitGene;
0046 import io.jenetics.DoubleChromosome;
0047 import io.jenetics.DoubleGene;
0048 import io.jenetics.EnumGene;
0049 import io.jenetics.Gene;
0050 import io.jenetics.Genotype;
0051 import io.jenetics.IntegerChromosome;
0052 import io.jenetics.IntegerGene;
0053 import io.jenetics.LongChromosome;
0054 import io.jenetics.LongGene;
0055 import io.jenetics.PermutationChromosome;
0056 import io.jenetics.internal.math.Combinatorics;
0057 import io.jenetics.internal.util.Bits;
0058 import io.jenetics.internal.util.Predicates;
0059 import io.jenetics.internal.util.Requires;
0060 import io.jenetics.util.DoubleRange;
0061 import io.jenetics.util.ISeq;
0062 import io.jenetics.util.IntRange;
0063 import io.jenetics.util.LongRange;
0064 
0065 /**
0066  * This class contains factory methods for creating common  problem encodings.
0067  *
0068  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
0069  @since 3.2
0070  @version 5.2
0071  */
0072 public final class Codecs {
0073 
0074     private Codecs() {}
0075 
0076     /**
0077      * Return a scalar {@link InvertibleCodec} for the given range.
0078      *
0079      @param domain the domain of the returned {@code Codec}
0080      @return a new scalar {@code Codec} with the given domain.
0081      @throws NullPointerException if the given {@code domain} is {@code null}
0082      */
0083     public static InvertibleCodec<Integer, IntegerGene>
0084     ofScalar(final IntRange domain) {
0085         requireNonNull(domain);
0086 
0087         return InvertibleCodec.of(
0088             Genotype.of(IntegerChromosome.of(domain)),
0089             gt -> gt.chromosome().gene().allele(),
0090             val -> Genotype.of(IntegerChromosome.of(IntegerGene.of(val, domain)))
0091         );
0092     }
0093 
0094     /**
0095      * Return a scalar {@link InvertibleCodec} for the given range.
0096      *
0097      @param domain the domain of the returned {@code Codec}
0098      @return a new scalar {@code Codec} with the given domain.
0099      @throws NullPointerException if the given {@code domain} is {@code null}
0100      */
0101     public static InvertibleCodec<Long, LongGene>
0102     ofScalar(final LongRange domain) {
0103         requireNonNull(domain);
0104 
0105         return InvertibleCodec.of(
0106             Genotype.of(LongChromosome.of(domain)),
0107             gt -> gt.gene().allele(),
0108             val -> Genotype.of(LongChromosome.of(LongGene.of(val, domain)))
0109         );
0110     }
0111 
0112     /**
0113      * Return a scalar {@link InvertibleCodec} for the given range.
0114      *
0115      @param domain the domain of the returned {@code Codec}
0116      @return a new scalar {@code Codec} with the given domain.
0117      @throws NullPointerException if the given {@code domain} is {@code null}
0118      */
0119     public static InvertibleCodec<Double, DoubleGene>
0120     ofScalar(final DoubleRange domain) {
0121         requireNonNull(domain);
0122 
0123         return InvertibleCodec.of(
0124             Genotype.of(DoubleChromosome.of(domain)),
0125             gt -> gt.gene().allele(),
0126             val -> Genotype.of(DoubleChromosome.of(DoubleGene.of(val, domain)))
0127         );
0128     }
0129 
0130     /**
0131      * Return a scala {@link Codec} with the given allele {@link Supplier} and
0132      * allele {@code validator}. The {@code supplier} is responsible for
0133      * creating new random alleles, and the {@code validator} can verify it.
0134      <p>
0135      * The following example shows a codec which creates and verifies
0136      * {@code BigInteger} objects.
0137      <pre>{@code
0138      * final Codec<BigInteger, AnyGene<BigInteger>> codec = Codecs.of(
0139      *     // Create new random 'BigInteger' object.
0140      *     () -> {
0141      *         final byte[] data = new byte[100];
0142      *         RandomRegistry.getRandom().nextBytes(data);
0143      *         return new BigInteger(data);
0144      *     },
0145      *     // Verify that bit 7 is set. (For illustration purpose.)
0146      *     bi -> bi.testBit(7)
0147      * );
0148      * }</pre>
0149      *
0150      @see AnyGene#of(Supplier, Predicate)
0151      @see AnyChromosome#of(Supplier, Predicate)
0152      *
0153      @param <A> the allele type
0154      @param supplier the allele-supplier which is used for creating new,
0155      *        random alleles
0156      @param validator the validator used for validating the created gene. This
0157      *        predicate is used in the {@link AnyGene#isValid()} method.
0158      @return a new {@code Codec} with the given parameters
0159      @throws NullPointerException if one of the parameters is {@code null}
0160      */
0161     public static <A> Codec<A, AnyGene<A>> ofScalar(
0162         final Supplier<? extends A> supplier,
0163         final Predicate<? super A> validator
0164     ) {
0165         return Codec.of(
0166             Genotype.of(AnyChromosome.of(supplier, validator)),
0167             gt -> gt.gene().allele()
0168         );
0169     }
0170 
0171     /**
0172      * Return a scala {@link Codec} with the given allele {@link Supplier} and
0173      * allele {@code validator}. The {@code supplier} is responsible for
0174      * creating new random alleles.
0175      *
0176      @see #ofScalar(Supplier, Predicate)
0177      @see AnyGene#of(Supplier)
0178      @see AnyChromosome#of(Supplier)
0179      *
0180      @param <A> the allele type
0181      @param supplier the allele-supplier which is used for creating new,
0182      *        random alleles
0183      @return a new {@code Codec} with the given parameters
0184      @throws NullPointerException if the parameter is {@code null}
0185      */
0186     public static <A> Codec<A, AnyGene<A>> ofScalar(
0187         final Supplier<? extends A> supplier
0188     ) {
0189         return Codec.of(
0190             Genotype.of(AnyChromosome.of(supplier)),
0191             gt -> gt.gene().allele()
0192         );
0193     }
0194 
0195     /**
0196      * Return a vector {@link InvertibleCodec} for the given range. All vector
0197      * values are restricted by the same domain.
0198      *
0199      @param domain the domain of the vector values
0200      @param length the vector length
0201      @return a new vector {@code Codec}
0202      @throws NullPointerException if the given {@code domain} is {@code null}
0203      @throws IllegalArgumentException if the {@code length} is smaller than
0204      *         one.
0205      */
0206     public static InvertibleCodec<int[], IntegerGene> ofVector(
0207         final IntRange domain,
0208         final int length
0209     ) {
0210         requireNonNull(domain);
0211         Requires.positive(length);
0212 
0213         return InvertibleCodec.of(
0214             Genotype.of(IntegerChromosome.of(domain, length)),
0215             gt -> gt.chromosome().as(IntegerChromosome.class).toArray(),
0216             val -> Genotype.of(
0217                 IntegerChromosome.of(
0218                     IntStream.of(val)
0219                         .mapToObj(i -> IntegerGene.of(i, domain))
0220                         .collect(ISeq.toISeq())
0221                 )
0222             )
0223         );
0224     }
0225 
0226     /**
0227      * Return a vector {@link InvertibleCodec} for the given range. All vector
0228      * values are restricted by the same domain.
0229      *
0230      @param domain the domain of the vector values
0231      @param length the vector length
0232      @return a new vector {@code Codec}
0233      @throws NullPointerException if the given {@code domain} is {@code null}
0234      @throws IllegalArgumentException if the {@code length} is smaller than
0235      *         one.
0236      */
0237     public static InvertibleCodec<long[], LongGene> ofVector(
0238         final LongRange domain,
0239         final int length
0240     ) {
0241         requireNonNull(domain);
0242         Requires.positive(length);
0243 
0244         return InvertibleCodec.of(
0245             Genotype.of(LongChromosome.of(domain, length)),
0246             gt -> gt.chromosome().as(LongChromosome.class).toArray(),
0247             val -> Genotype.of(
0248                 LongChromosome.of(
0249                     LongStream.of(val)
0250                         .mapToObj(l -> LongGene.of(l, domain))
0251                         .collect(ISeq.toISeq())
0252                 )
0253             )
0254         );
0255     }
0256 
0257     /**
0258      * Return a vector {@link InvertibleCodec} for the given range. All vector
0259      * values are restricted by the same domain.
0260      *
0261      @param domain the domain of the vector values
0262      @param length the vector length
0263      @return a new vector {@code Codec}
0264      @throws NullPointerException if the given {@code domain} is {@code null}
0265      @throws IllegalArgumentException if the {@code length} is smaller than
0266      *         one.
0267      */
0268     public static InvertibleCodec<double[], DoubleGene> ofVector(
0269         final DoubleRange domain,
0270         final int length
0271     ) {
0272         requireNonNull(domain);
0273         Requires.positive(length);
0274 
0275         return InvertibleCodec.of(
0276             Genotype.of(DoubleChromosome.of(domain, length)),
0277             gt -> gt.chromosome().as(DoubleChromosome.class).toArray(),
0278             val -> Genotype.of(
0279                 DoubleChromosome.of(
0280                     DoubleStream.of(val)
0281                         .mapToObj(d -> DoubleGene.of(d, domain))
0282                         .collect(ISeq.toISeq())
0283                 )
0284             )
0285         );
0286     }
0287 
0288     /**
0289      * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
0290      * element might have a different domain. The vector length is equal to the
0291      * number of domains.
0292      *
0293      @param domains the domain ranges
0294      @return a new vector {@code Codec}
0295      @throws NullPointerException if one of the arguments is {@code null}
0296      @throws IllegalArgumentException if the {@code domains} array is empty
0297      */
0298     public static InvertibleCodec<int[], IntegerGene>
0299     ofVector(final IntRange... domains) {
0300         if (domains.length == 0) {
0301             throw new IllegalArgumentException("Domains must not be empty.");
0302         }
0303 
0304         final ISeq<IntegerChromosome> chromosomes = Stream.of(domains)
0305             .peek(Objects::requireNonNull)
0306             .map(IntegerGene::of)
0307             .map(IntegerChromosome::of)
0308             .collect(ISeq.toISeq());
0309 
0310         return InvertibleCodec.of(
0311             Genotype.of(chromosomes),
0312             gt -> {
0313                 final int[] args = new int[gt.length()];
0314                 for (int i = gt.length(); --i >= 0;) {
0315                     args[i= gt.get(i).gene().intValue();
0316                 }
0317                 return args;
0318             },
0319             val -> Genotype.of(
0320                 IntStream.range(0, val.length)
0321                     .mapToObj(i ->
0322                         IntegerChromosome.of(IntegerGene.of(val[i], domains[i])))
0323                     .collect(ISeq.toISeq())
0324             )
0325         );
0326     }
0327 
0328     /**
0329      * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
0330      * element might have a different domain. The vector length is equal to the
0331      * number of domains.
0332      *
0333      @param domains the domain ranges
0334      @return a new vector {@code Codec}
0335      @throws NullPointerException if one of the arguments is {@code null}
0336      @throws IllegalArgumentException if the {@code domains} array is empty
0337      */
0338     public static InvertibleCodec<long[], LongGene>
0339     ofVector(final LongRange... domains) {
0340         if (domains.length == 0) {
0341             throw new IllegalArgumentException("Domains must not be empty.");
0342         }
0343 
0344         final ISeq<LongChromosome> chromosomes = Stream.of(domains)
0345             .peek(Objects::requireNonNull)
0346             .map(LongGene::of)
0347             .map(LongChromosome::of)
0348             .collect(ISeq.toISeq());
0349 
0350         return InvertibleCodec.of(
0351             Genotype.of(chromosomes),
0352             gt -> {
0353                 final long[] args = new long[gt.length()];
0354                 for (int i = gt.length(); --i >= 0;) {
0355                     args[i= gt.get(i).gene().longValue();
0356                 }
0357                 return args;
0358             },
0359             val -> Genotype.of(
0360                 IntStream.range(0, val.length)
0361                     .mapToObj(i ->
0362                         LongChromosome.of(LongGene.of(val[i], domains[i])))
0363                     .collect(ISeq.toISeq())
0364             )
0365         );
0366     }
0367 
0368     /**
0369      * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
0370      * element might have a different domain. The vector length is equal to the
0371      * number of domains.
0372      *
0373      @param domains the domain ranges
0374      @return a new vector {@code Codec}
0375      @throws NullPointerException if one of the arguments is {@code null}
0376      @throws IllegalArgumentException if the {@code domains} array is empty
0377      */
0378     public static InvertibleCodec<double[], DoubleGene>
0379     ofVector(final DoubleRange... domains) {
0380         if (domains.length == 0) {
0381             throw new IllegalArgumentException("Domains must not be empty.");
0382         }
0383 
0384         final ISeq<DoubleChromosome> chromosomes = Stream.of(domains)
0385             .peek(Objects::requireNonNull)
0386             .map(DoubleGene::of)
0387             .map(DoubleChromosome::of)
0388             .collect(ISeq.toISeq());
0389 
0390         return InvertibleCodec.of(
0391             Genotype.of(chromosomes),
0392             gt -> {
0393                 final double[] args = new double[gt.length()];
0394                 for (int i = gt.length(); --i >= 0;) {
0395                     args[i= gt.get(i).gene().doubleValue();
0396                 }
0397                 return args;
0398             },
0399             val -> Genotype.of(
0400                 IntStream.range(0, val.length)
0401                     .mapToObj(i ->
0402                         DoubleChromosome.of(DoubleGene.of(val[i], domains[i])))
0403                     .collect(ISeq.toISeq())
0404             )
0405         );
0406     }
0407 
0408     /**
0409      * Return a scala {@link Codec} with the given allele {@link Supplier},
0410      * allele {@code validator} and {@code Chromosome} length. The
0411      * {@code supplier} is responsible for creating new random alleles, and the
0412      * {@code validator} can verify it.
0413      <p>
0414      * The following example shows a codec which creates and verifies
0415      * {@code BigInteger} object arrays.
0416      <pre>{@code
0417      * final Codec<BigInteger[], AnyGene<BigInteger>> codec = Codecs.of(
0418      *     // Create new random 'BigInteger' object.
0419      *     () -> {
0420      *         final byte[] data = new byte[100];
0421      *         RandomRegistry.getRandom().nextBytes(data);
0422      *         return new BigInteger(data);
0423      *     },
0424      *     // Verify that bit 7 is set. (For illustration purpose.)
0425      *     bi -> bi.testBit(7),
0426      *     // The 'Chromosome' length.
0427      *     123
0428      * );
0429      * }</pre>
0430      *
0431      @see AnyChromosome#of(Supplier, Predicate, Predicate, int)
0432      *
0433      @param <A> the allele type
0434      @param supplier the allele-supplier which is used for creating new,
0435      *        random alleles
0436      @param alleleValidator the validator used for validating the created gene.
0437      *        This predicate is used in the {@link AnyGene#isValid()} method.
0438      @param alleleSeqValidator the validator used for validating the created
0439      *        chromosome. This predicate is used in the
0440      *        {@link AnyChromosome#isValid()} method.
0441      @param length the vector length
0442      @return a new {@code Codec} with the given parameters
0443      @throws NullPointerException if one of the parameters is {@code null}
0444      @throws IllegalArgumentException if the length of the vector is smaller
0445      *         than one.
0446      */
0447     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
0448         final Supplier<? extends A> supplier,
0449         final Predicate<? super A> alleleValidator,
0450         final Predicate<? super ISeq<A>> alleleSeqValidator,
0451         final int length
0452     ) {
0453         requireNonNull(supplier);
0454         requireNonNull(alleleSeqValidator);
0455         requireNonNull(alleleSeqValidator);
0456         Requires.positive(length);
0457 
0458         return Codec.of(
0459             Genotype.of(AnyChromosome
0460                 .of(supplier, alleleValidator, alleleSeqValidator, length)),
0461             gt -> gt.chromosome().stream()
0462                 .map(Gene::allele)
0463                 .collect(ISeq.toISeq())
0464         );
0465     }
0466 
0467     /**
0468      * Return a scala {@link Codec} with the given allele {@link Supplier},
0469      * allele {@code validator} and {@code Chromosome} length. The
0470      * {@code supplier} is responsible for creating new random alleles, and the
0471      * {@code validator} can verify it.
0472      *
0473      @param <A> the allele type
0474      @param supplier the allele-supplier which is used for creating new,
0475      *        random alleles
0476      @param validator the validator used for validating the created gene. This
0477      *        predicate is used in the {@link AnyGene#isValid()} method.
0478      @param length the vector length
0479      @return a new {@code Codec} with the given parameters
0480      @throws NullPointerException if one of the parameters is {@code null}
0481      @throws IllegalArgumentException if the length of the vector is smaller
0482      *         than one.
0483      */
0484     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
0485         final Supplier<? extends A> supplier,
0486         final Predicate<? super A> validator,
0487         final int length
0488     ) {
0489         return ofVector(
0490             supplier,
0491             validator,
0492             Predicates.True(),
0493             length
0494         );
0495     }
0496 
0497     /**
0498      * Return a scala {@link Codec} with the given allele {@link Supplier} and
0499      * {@code Chromosome} length. The {@code supplier} is responsible for
0500      * creating new random alleles.
0501      *
0502      @param <A> the allele type
0503      @param supplier the allele-supplier which is used for creating new,
0504      *        random alleles
0505      @param length the vector length
0506      @return a new {@code Codec} with the given parameters
0507      @throws NullPointerException if one of the parameters is {@code null}
0508      @throws IllegalArgumentException if the length of the vector is smaller
0509      *         than one.
0510      */
0511     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
0512         final Supplier<? extends A> supplier,
0513         final int length
0514     ) {
0515         return ofVector(supplier, Predicates.TRUE, length);
0516     }
0517 
0518     /**
0519      * Create a permutation {@link InvertibleCodec} of integer in the range
0520      * {@code [0, length)}.
0521      *
0522      @param length the number of permutation elements
0523      @return a permutation {@code Codec} of integers
0524      @throws IllegalArgumentException if the {@code length} is smaller than
0525      *         one.
0526      */
0527     public static InvertibleCodec<int[], EnumGene<Integer>>
0528     ofPermutation(final int length) {
0529         Requires.positive(length);
0530 
0531         final PermutationChromosome<Integer> chromosome =
0532             PermutationChromosome.ofInteger(length);
0533 
0534         final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
0535             .collect(Collectors.toMap(EnumGene::allele, identity()));
0536 
0537         return InvertibleCodec.of(
0538             Genotype.of(chromosome),
0539             gt -> gt.chromosome().stream()
0540                 .mapToInt(EnumGene::allele)
0541                 .toArray(),
0542             val -> Genotype.of(
0543                 new PermutationChromosome<>(
0544                     IntStream.of(val)
0545                         .mapToObj(genes::get)
0546                         .collect(ISeq.toISeq())
0547                 )
0548             )
0549         );
0550     }
0551 
0552     /**
0553      * Create a permutation {@link InvertibleCodec} with the given alleles.
0554      *
0555      @param alleles the alleles of the permutation
0556      @param <T> the allele type
0557      @return a new permutation {@code Codec}
0558      @throws IllegalArgumentException if the given allele array is empty
0559      @throws NullPointerException if one of the alleles is {@code null}
0560      */
0561     public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>>
0562     ofPermutation(final ISeq<? extends T> alleles) {
0563         if (alleles.isEmpty()) {
0564             throw new IllegalArgumentException(
0565                 "Empty allele array is not allowed."
0566             );
0567         }
0568 
0569         final Map<T, EnumGene<T>> genes =
0570             IntStream.range(0, alleles.length())
0571                 .mapToObj(i -> EnumGene.<T>of(i, alleles))
0572                 .collect(Collectors.toMap(EnumGene::allele, identity()));
0573 
0574         return InvertibleCodec.of(
0575             Genotype.of(new PermutationChromosome<>(ISeq.of(genes.values()))),
0576             gt -> gt.chromosome().stream()
0577                 .map(EnumGene::allele)
0578                 .collect(ISeq.toISeq()),
0579             val -> Genotype.of(
0580                 new PermutationChromosome<>(
0581                     val.stream()
0582                         .map(genes::get)
0583                         .collect(ISeq.toISeq())
0584                 )
0585             )
0586         );
0587     }
0588 
0589     /**
0590      * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
0591      * All matrix values are restricted by the same domain. The dimension of the
0592      * returned matrix is {@code int[rows][cols]}.
0593      *
0594      @since 4.4
0595      *
0596      @param domain the domain of the matrix values
0597      @param rows the number of rows of the matrix
0598      @param cols the number of columns of the matrix
0599      @return a new matrix {@code Codec}
0600      @throws NullPointerException if the given {@code domain} is {@code null}
0601      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
0602      *         smaller than one.
0603      */
0604     public static InvertibleCodec<int[][], IntegerGene> ofMatrix(
0605         final IntRange domain,
0606         final int rows,
0607         final int cols
0608     ) {
0609         requireNonNull(domain);
0610         Requires.positive(rows);
0611         Requires.positive(cols);
0612 
0613         return InvertibleCodec.of(
0614             Genotype.of(
0615                 IntegerChromosome.of(domain, cols).instances()
0616                     .limit(rows)
0617                     .collect(ISeq.toISeq())
0618             ),
0619             gt -> gt.stream()
0620                 .map(ch -> ch.stream()
0621                     .mapToInt(IntegerGene::intValue)
0622                     .toArray())
0623                 .toArray(int[][]::new),
0624             matrix -> Genotype.of(
0625                 Stream.of(matrix)
0626                     .map(row ->
0627                         IntegerChromosome.of(
0628                             IntStream.of(row)
0629                                 .mapToObj(v -> IntegerGene.of(v, domain))
0630                                 .collect(ISeq.toISeq())
0631                         ))
0632                     .collect(ISeq.toISeq())
0633             )
0634         );
0635     }
0636 
0637     /**
0638      * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
0639      * All matrix values are restricted by the same domain. The dimension of the
0640      * returned matrix is {@code long[rows][cols]}.
0641      *
0642      @since 4.4
0643      *
0644      @param domain the domain of the matrix values
0645      @param rows the number of rows of the matrix
0646      @param cols the number of columns of the matrix
0647      @return a new matrix {@code Codec}
0648      @throws NullPointerException if the given {@code domain} is {@code null}
0649      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
0650      *         smaller than one.
0651      */
0652     public static InvertibleCodec<long[][], LongGene> ofMatrix(
0653         final LongRange domain,
0654         final int rows,
0655         final int cols
0656     ) {
0657         requireNonNull(domain);
0658         Requires.positive(rows);
0659         Requires.positive(cols);
0660 
0661         return InvertibleCodec.of(
0662             Genotype.of(
0663                 LongChromosome.of(domain, cols).instances()
0664                     .limit(rows)
0665                     .collect(ISeq.toISeq())
0666             ),
0667             gt -> gt.stream()
0668                 .map(ch -> ch.stream()
0669                     .mapToLong(LongGene::longValue)
0670                     .toArray())
0671                 .toArray(long[][]::new),
0672             matrix -> Genotype.of(
0673                 Stream.of(matrix)
0674                     .map(row ->
0675                         LongChromosome.of(
0676                             LongStream.of(row)
0677                                 .mapToObj(v -> LongGene.of(v, domain))
0678                                 .collect(ISeq.toISeq())
0679                         ))
0680                     .collect(ISeq.toISeq())
0681             )
0682         );
0683     }
0684 
0685     /**
0686      * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
0687      * All matrix values are restricted by the same domain. The dimension of the
0688      * returned matrix is {@code double[rows][cols]}.
0689      *
0690      @since 4.4
0691      *
0692      @param domain the domain of the matrix values
0693      @param rows the number of rows of the matrix
0694      @param cols the number of columns of the matrix
0695      @return a new matrix {@code Codec}
0696      @throws NullPointerException if the given {@code domain} is {@code null}
0697      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
0698      *         smaller than one.
0699      */
0700     public static InvertibleCodec<double[][], DoubleGene> ofMatrix(
0701         final DoubleRange domain,
0702         final int rows,
0703         final int cols
0704     ) {
0705         requireNonNull(domain);
0706         Requires.positive(rows);
0707         Requires.positive(cols);
0708 
0709         return InvertibleCodec.of(
0710             Genotype.of(
0711                 DoubleChromosome.of(domain, cols).instances()
0712                     .limit(rows)
0713                     .collect(ISeq.toISeq())
0714             ),
0715             gt -> gt.stream()
0716                 .map(ch -> ch.stream()
0717                     .mapToDouble(DoubleGene::doubleValue)
0718                     .toArray())
0719                 .toArray(double[][]::new),
0720             matrix -> Genotype.of(
0721                 Stream.of(matrix)
0722                     .map(row ->
0723                         DoubleChromosome.of(
0724                             DoubleStream.of(row)
0725                                 .mapToObj(v -> DoubleGene.of(v, domain))
0726                                 .collect(ISeq.toISeq())
0727                         ))
0728                     .collect(ISeq.toISeq())
0729             )
0730         );
0731     }
0732 
0733     /**
0734      * Create a codec, which creates a a mapping from the elements given in the
0735      * {@code source} sequence to the elements given in the {@code target}
0736      * sequence. The returned mapping can be seen as a function which maps every
0737      * element of the {@code target} set to an element of the {@code source} set.
0738      *
0739      <pre>{@code
0740      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
0741      * final ISeq<String> strings = ISeq.of("1", "2", "3");
0742      *
0743      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
0744      *     Codecs.ofMapping(numbers, strings, HashMap::new);
0745      * }</pre>
0746      *
0747      * If {@code source.size() > target.size()}, the created mapping is
0748      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
0749      * if {@code source.size() < target.size()}, the mapping is
0750      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
0751      * and if both sets have the same size, the returned mapping is
0752      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
0753      *
0754      @since 4.3
0755      *
0756      @param source the source elements. Will be the <em>keys</em> of the
0757      *        encoded {@code Map}.
0758      @param target the target elements. Will be the <em>values</em> of the
0759      *           encoded {@code Map}.
0760      @param mapSupplier a function which returns a new, empty Map into which
0761      *        the mapping will be inserted
0762      @param <A> the type of the source elements
0763      @param <B> the type of the target elements
0764      @param <M> the type of the encoded Map
0765      @return a new mapping codec
0766      @throws IllegalArgumentException if the {@code target} sequences are empty
0767      @throws NullPointerException if one of the argument is {@code null}
0768      */
0769     public static <A, B, M extends Map<A, B>> InvertibleCodec<M, EnumGene<Integer>>
0770     ofMapping(
0771         final ISeq<? extends A> source,
0772         final ISeq<? extends B> target,
0773         final Supplier<M> mapSupplier
0774     ) {
0775         requireNonNull(source);
0776         requireNonNull(target);
0777         requireNonNull(mapSupplier);
0778 
0779         final Map<A, Integer> smap = IntStream.range(0, source.length())
0780             .mapToObj(i -> entry(source.get(i), i))
0781             .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
0782 
0783         final Map<B, Integer> tmap = IntStream.range(0, target.length())
0784             .mapToObj(i -> entry(target.get(i), i))
0785             .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
0786 
0787         final PermutationChromosome<Integer> chromosome =
0788             PermutationChromosome.ofInteger(target.size());
0789 
0790         final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
0791             .collect(Collectors.toMap(EnumGene::allele, identity()));
0792 
0793         final Codec<int[], EnumGene<Integer>> codec = Codec.of(
0794             Genotype.of(chromosome),
0795             gt -> gt.chromosome().stream()
0796                 .mapToInt(EnumGene::allele)
0797                 .toArray()
0798         );
0799 
0800         return codec
0801             .map(permutation -> toMapping(permutation, source, target, mapSupplier))
0802             .toInvertibleCodec(mapping -> toEncoding(mapping, smap,tmap, genes));
0803     }
0804 
0805     private static <A, B> Map.Entry<A, B> entry(final A a, final B b) {
0806         return new SimpleImmutableEntry<>(a, b);
0807     }
0808 
0809     private static <A, B, M extends Map<A, B>> M toMapping(
0810         final int[] perm,
0811         final ISeq<? extends A> source,
0812         final ISeq<? extends B> target,
0813         final Supplier<M> mapSupplier
0814     ) {
0815         return IntStream.range(0, source.size())
0816             .mapToObj(i -> entry(source.get(i), target.get(perm[i%perm.length])))
0817             .collect(Collectors.toMap(
0818                 Entry::getKey, Entry::getValue,
0819                 (u,v-> {throw new IllegalStateException("Duplicate key " + u);},
0820                 mapSupplier));
0821     }
0822 
0823     private static <A, B> Genotype<EnumGene<Integer>> toEncoding(
0824         final Map<A, B> mapping,
0825         final Map<A, Integer> source,
0826         final Map<B, Integer> target,
0827         final Map<Integer, EnumGene<Integer>> genes
0828     ) {
0829         final int[] perm = new int[target.size()];
0830         source.forEach((key, value-> {
0831             final int i = value;
0832             final int j = target.get(mapping.get(key));
0833             perm[i%perm.length= j;
0834         });
0835 
0836         // Fill the rest of the 'perm' array, without duplicates.
0837         // TODO: can be done more efficiently
0838         if (target.size() > source.size()) {
0839             final Set<Integer> indexes = new HashSet<>();
0840             for (int i = 0; i < target.size(); ++i) {
0841                 indexes.add(i);
0842             }
0843 
0844             for (int i = 0; i < source.size(); ++i) {
0845                 indexes.remove(perm[i]);
0846             }
0847 
0848             final Iterator<Integer> it = indexes.iterator();
0849             for (int i = source.size(); i < target.size(); ++i) {
0850                 perm[i= it.next();
0851                 it.remove();
0852             }
0853         }
0854 
0855         return  Genotype.of(
0856             new PermutationChromosome<>(
0857                 IntStream.of(perm)
0858                     .mapToObj(genes::get)
0859                     .collect(ISeq.toISeq())
0860             )
0861         );
0862     }
0863 
0864     /**
0865      * Create a codec, which creates a a mapping from the elements given in the
0866      * {@code source} sequence to the elements given in the {@code target}
0867      * sequence. The returned mapping can be seen as a function which maps every
0868      * element of the {@code target} set to an element of the {@code source} set.
0869      *
0870      <pre>{@code
0871      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
0872      * final ISeq<String> strings = ISeq.of("1", "2", "3");
0873      *
0874      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
0875      *     Codecs.ofMapping(numbers, strings);
0876      * }</pre>
0877      *
0878      * If {@code source.size() > target.size()}, the created mapping is
0879      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
0880      * if {@code source.size() < target.size()}, the mapping is
0881      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
0882      * and if both sets have the same size, the returned mapping is
0883      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
0884      *
0885      @since 4.3
0886      *
0887      @param source the source elements. Will be the <em>keys</em> of the
0888      *        encoded {@code Map}.
0889      @param target the target elements. Will be the <em>values</em> of the
0890      *           encoded {@code Map}.
0891      @param <A> the type of the source elements
0892      @param <B> the type of the target elements
0893      @return a new mapping codec
0894      @throws IllegalArgumentException if the {@code target} sequences are empty
0895      @throws NullPointerException if one of the argument is {@code null}
0896      */
0897     public static <A, B> InvertibleCodec<Map<A, B>, EnumGene<Integer>>
0898     ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
0899         return ofMapping(source, target, HashMap::new);
0900     }
0901 
0902     /**
0903      * The subset {@link InvertibleCodec} can be used for problems where it is
0904      * required to find the best <b>variable-sized</b> subset from given basic
0905      * set. A typical usage example of the returned {@code Codec} is the
0906      * Knapsack problem.
0907      <p>
0908      * The following code snippet shows a simplified variation of the Knapsack
0909      * problem.
0910      <pre>{@code
0911      * public final class Main {
0912      *     // The basic set from where to choose an 'optimal' subset.
0913      *     private final static ISeq<Integer> SET =
0914      *         ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
0915      *
0916      *     // Fitness function directly takes an 'int' value.
0917      *     private static int fitness(final ISeq<Integer> subset) {
0918      *         assert(subset.size() <= SET.size());
0919      *         final int size = subset.stream()
0920      *             .collect(Collectors.summingInt(Integer::intValue));
0921      *         return size <= 20 ? size : 0;
0922      *     }
0923      *
0924      *     public static void main(final String[] args) {
0925      *         final Engine<BitGene, Double> engine = Engine
0926      *             .builder(Main::fitness, codec.ofSubSet(SET))
0927      *             .build();
0928      *         ...
0929      *     }
0930      * }
0931      * }</pre>
0932      *
0933      @param <T> the element type of the basic set
0934      @param basicSet the basic set, from where to choose the <i>optimal</i>
0935      *        subset.
0936      @return a new codec which can be used for modelling <i>subset</i>
0937      *         problems.
0938      @throws NullPointerException if the given {@code basicSet} is
0939      *         {@code null}; {@code null} elements are allowed.
0940      @throws IllegalArgumentException if the {@code basicSet} size is smaller
0941      *         than one.
0942      */
0943     public static <T> InvertibleCodec<ISeq<T>, BitGene>
0944     ofSubSet(final ISeq<? extends T> basicSet) {
0945         requireNonNull(basicSet);
0946         Requires.positive(basicSet.length());
0947 
0948         return InvertibleCodec.of(
0949             Genotype.of(BitChromosome.of(basicSet.length())),
0950             gt -> gt.chromosome()
0951                 .as(BitChromosome.class).ones()
0952                 .<T>mapToObj(basicSet)
0953                 .collect(ISeq.toISeq()),
0954             values -> {
0955                 final byte[] bits = Bits.newArray(basicSet.size());
0956 
0957                 int i = 0;
0958                 for (T v : values) {
0959                     while (i < basicSet.size() && !Objects.equals(basicSet.get(i), v)) {
0960                         ++i;
0961                     }
0962                     Bits.set(bits, i);
0963                 }
0964 
0965                 return Genotype.of(new BitChromosome(bits, 0, basicSet.size()));
0966             }
0967         );
0968     }
0969 
0970     /**
0971      * The subset {@link InvertibleCodec} can be used for problems where it is
0972      * required to find the best <b>fixed-size</b> subset from given basic set.
0973      *
0974      @since 3.4
0975      *
0976      @see PermutationChromosome
0977      @see PermutationChromosome#of(ISeq, int)
0978      *
0979      @param <T> the element type of the basic set
0980      @param basicSet the basic set, from where to choose the <i>optimal</i>
0981      *        subset.
0982      @param size the length of the desired subsets
0983      @return a new codec which can be used for modelling <i>subset</i>
0984      *         problems.
0985      @throws NullPointerException if the given {@code basicSet} is
0986      *         {@code null}; {@code null} elements are allowed.
0987      @throws IllegalArgumentException if {@code basicSet.size() < size},
0988      *         {@code size <= 0} or {@code basicSet.size()*size} will cause an
0989      *         integer overflow.
0990      */
0991     public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> ofSubSet(
0992         final ISeq<? extends T> basicSet,
0993         final int size
0994     ) {
0995         requireNonNull(basicSet);
0996         Combinatorics.checkSubSet(basicSet.size(), size);
0997 
0998         final Map<T, EnumGene<T>> genes =
0999             IntStream.range(0, basicSet.length())
1000                 .mapToObj(i -> EnumGene.<T>of(i, basicSet))
1001                 .collect(Collectors.toMap(EnumGene::allele, identity()));
1002 
1003         return InvertibleCodec.of(
1004             Genotype.of(PermutationChromosome.of(basicSet, size)),
1005             gt -> gt.chromosome().stream()
1006                 .map(EnumGene::allele)
1007                 .collect(ISeq.toISeq()),
1008             values -> {
1009                 if (values.size() != size) {
1010                     throw new IllegalArgumentException(format(
1011                         "Expected sub-set size of %d, but got %d,",
1012                         size, values.size()
1013                     ));
1014                 }
1015 
1016                 return Genotype.of(
1017                     new PermutationChromosome<>(
1018                         values.stream()
1019                             .map(genes::get)
1020                             .collect(ISeq.toISeq())
1021                     )
1022                 );
1023             }
1024         );
1025     }
1026 
1027 //    /**
1028 //     * Creates a codec for a 2-dimensional affine transformation. The composed
1029 //     * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
1030 //     *
1031 //     * @param sx the scale factor range in x direction
1032 //     * @param sy the scale factor range in y direction
1033 //     * @param tx the translation range in x direction
1034 //     * @param ty the translation range in y direction
1035 //     * @param th the rotation range (in radians)
1036 //     * @param kx the shear range in x direction
1037 //     * @param ky the shear range in x direction
1038 //     * @return the affine transformation codec
1039 //     * @throws NullPointerException if one of the arguments is {@code null}
1040 //     */
1041 //    static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1042 //        final DoubleRange sx, final DoubleRange sy,
1043 //        final DoubleRange tx, final DoubleRange ty,
1044 //        final DoubleRange th,
1045 //        final DoubleRange kx, final DoubleRange ky
1046 //    ) {
1047 //        return Codec.of(
1048 //            Genotype.of(
1049 //                // Scale
1050 //                DoubleChromosome.of(sx), DoubleChromosome.of(sy),
1051 //                // Translation
1052 //                DoubleChromosome.of(tx), DoubleChromosome.of(ty),
1053 //                // Rotation
1054 //                DoubleChromosome.of(th),
1055 //                // Shear
1056 //                DoubleChromosome.of(kx), DoubleChromosome.of(ky)
1057 //            ),
1058 //            gt -> {
1059 //                final AffineTransform at = new AffineTransform();
1060 //
1061 //                at.translate(
1062 //                    gt.getChromosome(2).getGene().doubleValue(),
1063 //                    gt.getChromosome(3).getGene().doubleValue()
1064 //                );
1065 //                at.shear(
1066 //                    gt.getChromosome(5).getGene().doubleValue(),
1067 //                    gt.getChromosome(6).getGene().doubleValue()
1068 //                );
1069 //                at.scale(
1070 //                    gt.getChromosome(0).getGene().doubleValue(),
1071 //                    gt.getChromosome(1).getGene().doubleValue()
1072 //                );
1073 //                at.rotate(gt.getChromosome(4).getGene().doubleValue());
1074 //
1075 //                return at;
1076 //            }
1077 //        );
1078 //    }
1079 //
1080 //    /**
1081 //     * Creates a codec for a 2-dimensional affine transformation. The composed
1082 //     * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
1083 //     *
1084 //     * @param s the scale factor range in x and y direction
1085 //     * @param t the translation range in x and y direction
1086 //     * @param th the rotation angle range
1087 //     * @param k the shear range in x and y direction
1088 //     * @return the affine transformation codec
1089 //     * @throws NullPointerException if one of the arguments is {@code null}
1090 //     */
1091 //    static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1092 //        final DoubleRange s,
1093 //        final DoubleRange t,
1094 //        final DoubleRange th,
1095 //        final DoubleRange k
1096 //    ) {
1097 //        return ofAffineTransform(s, s, t, t, th, k, k);
1098 //    }
1099 
1100 }