Codecs.java
0001 /*
0002  * Java Genetic Algorithm Library (jenetics-6.3.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.as(IntegerChromosome.class).toArray())
0621                 .toArray(int[][]::new),
0622             matrix -> Genotype.of(
0623                 Stream.of(matrix)
0624                     .map(row ->
0625                         IntegerChromosome.of(
0626                             IntStream.of(row)
0627                                 .mapToObj(v -> IntegerGene.of(v, domain))
0628                                 .collect(ISeq.toISeq())
0629                         ))
0630                     .collect(ISeq.toISeq())
0631             )
0632         );
0633     }
0634 
0635     /**
0636      * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
0637      * All matrix values are restricted by the same domain. The dimension of the
0638      * returned matrix is {@code long[rows][cols]}.
0639      *
0640      @since 4.4
0641      *
0642      @param domain the domain of the matrix values
0643      @param rows the number of rows of the matrix
0644      @param cols the number of columns of the matrix
0645      @return a new matrix {@code Codec}
0646      @throws NullPointerException if the given {@code domain} is {@code null}
0647      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
0648      *         smaller than one.
0649      */
0650     public static InvertibleCodec<long[][], LongGene> ofMatrix(
0651         final LongRange domain,
0652         final int rows,
0653         final int cols
0654     ) {
0655         requireNonNull(domain);
0656         Requires.positive(rows);
0657         Requires.positive(cols);
0658 
0659         return InvertibleCodec.of(
0660             Genotype.of(
0661                 LongChromosome.of(domain, cols).instances()
0662                     .limit(rows)
0663                     .collect(ISeq.toISeq())
0664             ),
0665             gt -> gt.stream()
0666                 .map(ch -> ch.as(LongChromosome.class).toArray())
0667                 .toArray(long[][]::new),
0668             matrix -> Genotype.of(
0669                 Stream.of(matrix)
0670                     .map(row ->
0671                         LongChromosome.of(
0672                             LongStream.of(row)
0673                                 .mapToObj(v -> LongGene.of(v, domain))
0674                                 .collect(ISeq.toISeq())
0675                         ))
0676                     .collect(ISeq.toISeq())
0677             )
0678         );
0679     }
0680 
0681     /**
0682      * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
0683      * All matrix values are restricted by the same domain. The dimension of the
0684      * returned matrix is {@code double[rows][cols]}.
0685      *
0686      @since 4.4
0687      *
0688      @param domain the domain of the matrix values
0689      @param rows the number of rows of the matrix
0690      @param cols the number of columns of the matrix
0691      @return a new matrix {@code Codec}
0692      @throws NullPointerException if the given {@code domain} is {@code null}
0693      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
0694      *         smaller than one.
0695      */
0696     public static InvertibleCodec<double[][], DoubleGene> ofMatrix(
0697         final DoubleRange domain,
0698         final int rows,
0699         final int cols
0700     ) {
0701         requireNonNull(domain);
0702         Requires.positive(rows);
0703         Requires.positive(cols);
0704 
0705         return InvertibleCodec.of(
0706             Genotype.of(
0707                 DoubleChromosome.of(domain, cols).instances()
0708                     .limit(rows)
0709                     .collect(ISeq.toISeq())
0710             ),
0711             gt -> gt.stream()
0712                 .map(ch -> ch.as(DoubleChromosome.class).toArray())
0713                 .toArray(double[][]::new),
0714             matrix -> Genotype.of(
0715                 Stream.of(matrix)
0716                     .map(row ->
0717                         DoubleChromosome.of(
0718                             DoubleStream.of(row)
0719                                 .mapToObj(v -> DoubleGene.of(v, domain))
0720                                 .collect(ISeq.toISeq())
0721                         ))
0722                     .collect(ISeq.toISeq())
0723             )
0724         );
0725     }
0726 
0727     /**
0728      * Create a codec, which creates a a mapping from the elements given in the
0729      * {@code source} sequence to the elements given in the {@code target}
0730      * sequence. The returned mapping can be seen as a function which maps every
0731      * element of the {@code target} set to an element of the {@code source} set.
0732      *
0733      <pre>{@code
0734      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
0735      * final ISeq<String> strings = ISeq.of("1", "2", "3");
0736      *
0737      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
0738      *     Codecs.ofMapping(numbers, strings, HashMap::new);
0739      * }</pre>
0740      *
0741      * If {@code source.size() > target.size()}, the created mapping is
0742      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
0743      * if {@code source.size() < target.size()}, the mapping is
0744      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
0745      * and if both sets have the same size, the returned mapping is
0746      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
0747      *
0748      @since 4.3
0749      *
0750      @param source the source elements. Will be the <em>keys</em> of the
0751      *        encoded {@code Map}.
0752      @param target the target elements. Will be the <em>values</em> of the
0753      *           encoded {@code Map}.
0754      @param mapSupplier a function which returns a new, empty Map into which
0755      *        the mapping will be inserted
0756      @param <A> the type of the source elements
0757      @param <B> the type of the target elements
0758      @param <M> the type of the encoded Map
0759      @return a new mapping codec
0760      @throws IllegalArgumentException if the {@code target} sequences are empty
0761      @throws NullPointerException if one of the argument is {@code null}
0762      */
0763     public static <A, B, M extends Map<A, B>> InvertibleCodec<M, EnumGene<Integer>>
0764     ofMapping(
0765         final ISeq<? extends A> source,
0766         final ISeq<? extends B> target,
0767         final Supplier<M> mapSupplier
0768     ) {
0769         requireNonNull(source);
0770         requireNonNull(target);
0771         requireNonNull(mapSupplier);
0772 
0773         final Map<A, Integer> smap = IntStream.range(0, source.length())
0774             .mapToObj(i -> entry(source.get(i), i))
0775             .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
0776 
0777         final Map<B, Integer> tmap = IntStream.range(0, target.length())
0778             .mapToObj(i -> entry(target.get(i), i))
0779             .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
0780 
0781         final PermutationChromosome<Integer> chromosome =
0782             PermutationChromosome.ofInteger(target.size());
0783 
0784         final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
0785             .collect(Collectors.toMap(EnumGene::allele, identity()));
0786 
0787         final Codec<int[], EnumGene<Integer>> codec = Codec.of(
0788             Genotype.of(chromosome),
0789             gt -> gt.chromosome().stream()
0790                 .mapToInt(EnumGene::allele)
0791                 .toArray()
0792         );
0793 
0794         return codec
0795             .map(permutation -> toMapping(permutation, source, target, mapSupplier))
0796             .toInvertibleCodec(mapping -> toEncoding(mapping, smap,tmap, genes));
0797     }
0798 
0799     private static <A, B> Map.Entry<A, B> entry(final A a, final B b) {
0800         return new SimpleImmutableEntry<>(a, b);
0801     }
0802 
0803     private static <A, B, M extends Map<A, B>> M toMapping(
0804         final int[] perm,
0805         final ISeq<? extends A> source,
0806         final ISeq<? extends B> target,
0807         final Supplier<M> mapSupplier
0808     ) {
0809         return IntStream.range(0, source.size())
0810             .mapToObj(i -> entry(source.get(i), target.get(perm[i%perm.length])))
0811             .collect(Collectors.toMap(
0812                 Entry::getKey, Entry::getValue,
0813                 (u,v-> {throw new IllegalStateException("Duplicate key " + u);},
0814                 mapSupplier));
0815     }
0816 
0817     private static <A, B> Genotype<EnumGene<Integer>> toEncoding(
0818         final Map<A, B> mapping,
0819         final Map<A, Integer> source,
0820         final Map<B, Integer> target,
0821         final Map<Integer, EnumGene<Integer>> genes
0822     ) {
0823         final int[] perm = new int[target.size()];
0824         source.forEach((key, value-> {
0825             final int i = value;
0826             final int j = target.get(mapping.get(key));
0827             perm[i%perm.length= j;
0828         });
0829 
0830         // Fill the rest of the 'perm' array, without duplicates.
0831         // TODO: can be done more efficiently
0832         if (target.size() > source.size()) {
0833             final Set<Integer> indexes = new HashSet<>();
0834             for (int i = 0; i < target.size(); ++i) {
0835                 indexes.add(i);
0836             }
0837 
0838             for (int i = 0; i < source.size(); ++i) {
0839                 indexes.remove(perm[i]);
0840             }
0841 
0842             final Iterator<Integer> it = indexes.iterator();
0843             for (int i = source.size(); i < target.size(); ++i) {
0844                 perm[i= it.next();
0845                 it.remove();
0846             }
0847         }
0848 
0849         return  Genotype.of(
0850             new PermutationChromosome<>(
0851                 IntStream.of(perm)
0852                     .mapToObj(genes::get)
0853                     .collect(ISeq.toISeq())
0854             )
0855         );
0856     }
0857 
0858     /**
0859      * Create a codec, which creates a a mapping from the elements given in the
0860      * {@code source} sequence to the elements given in the {@code target}
0861      * sequence. The returned mapping can be seen as a function which maps every
0862      * element of the {@code target} set to an element of the {@code source} set.
0863      *
0864      <pre>{@code
0865      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
0866      * final ISeq<String> strings = ISeq.of("1", "2", "3");
0867      *
0868      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
0869      *     Codecs.ofMapping(numbers, strings);
0870      * }</pre>
0871      *
0872      * If {@code source.size() > target.size()}, the created mapping is
0873      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
0874      * if {@code source.size() < target.size()}, the mapping is
0875      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
0876      * and if both sets have the same size, the returned mapping is
0877      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
0878      *
0879      @since 4.3
0880      *
0881      @param source the source elements. Will be the <em>keys</em> of the
0882      *        encoded {@code Map}.
0883      @param target the target elements. Will be the <em>values</em> of the
0884      *           encoded {@code Map}.
0885      @param <A> the type of the source elements
0886      @param <B> the type of the target elements
0887      @return a new mapping codec
0888      @throws IllegalArgumentException if the {@code target} sequences are empty
0889      @throws NullPointerException if one of the argument is {@code null}
0890      */
0891     public static <A, B> InvertibleCodec<Map<A, B>, EnumGene<Integer>>
0892     ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
0893         return ofMapping(source, target, HashMap::new);
0894     }
0895 
0896     /**
0897      * The subset {@link InvertibleCodec} can be used for problems where it is
0898      * required to find the best <b>variable-sized</b> subset from given basic
0899      * set. A typical usage example of the returned {@code Codec} is the
0900      * Knapsack problem.
0901      <p>
0902      * The following code snippet shows a simplified variation of the Knapsack
0903      * problem.
0904      <pre>{@code
0905      * public final class Main {
0906      *     // The basic set from where to choose an 'optimal' subset.
0907      *     private final static ISeq<Integer> SET =
0908      *         ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
0909      *
0910      *     // Fitness function directly takes an 'int' value.
0911      *     private static int fitness(final ISeq<Integer> subset) {
0912      *         assert(subset.size() <= SET.size());
0913      *         final int size = subset.stream()
0914      *             .collect(Collectors.summingInt(Integer::intValue));
0915      *         return size <= 20 ? size : 0;
0916      *     }
0917      *
0918      *     public static void main(final String[] args) {
0919      *         final Engine<BitGene, Double> engine = Engine
0920      *             .builder(Main::fitness, codec.ofSubSet(SET))
0921      *             .build();
0922      *         ...
0923      *     }
0924      * }
0925      * }</pre>
0926      *
0927      @param <T> the element type of the basic set
0928      @param basicSet the basic set, from where to choose the <i>optimal</i>
0929      *        subset.
0930      @return a new codec which can be used for modelling <i>subset</i>
0931      *         problems.
0932      @throws NullPointerException if the given {@code basicSet} is
0933      *         {@code null}; {@code null} elements are allowed.
0934      @throws IllegalArgumentException if the {@code basicSet} size is smaller
0935      *         than one.
0936      */
0937     public static <T> InvertibleCodec<ISeq<T>, BitGene>
0938     ofSubSet(final ISeq<? extends T> basicSet) {
0939         requireNonNull(basicSet);
0940         Requires.positive(basicSet.length());
0941 
0942         return InvertibleCodec.of(
0943             Genotype.of(BitChromosome.of(basicSet.length())),
0944             gt -> gt.chromosome()
0945                 .as(BitChromosome.class).ones()
0946                 .<T>mapToObj(basicSet)
0947                 .collect(ISeq.toISeq()),
0948             values -> {
0949                 final byte[] bits = Bits.newArray(basicSet.size());
0950 
0951                 int i = 0;
0952                 for (T v : values) {
0953                     while (i < basicSet.size() && !Objects.equals(basicSet.get(i), v)) {
0954                         ++i;
0955                     }
0956                     Bits.set(bits, i);
0957                 }
0958 
0959                 return Genotype.of(new BitChromosome(bits, 0, basicSet.size()));
0960             }
0961         );
0962     }
0963 
0964     /**
0965      * The subset {@link InvertibleCodec} can be used for problems where it is
0966      * required to find the best <b>fixed-size</b> subset from given basic set.
0967      *
0968      @since 3.4
0969      *
0970      @see PermutationChromosome
0971      @see PermutationChromosome#of(ISeq, int)
0972      *
0973      @param <T> the element type of the basic set
0974      @param basicSet the basic set, from where to choose the <i>optimal</i>
0975      *        subset.
0976      @param size the length of the desired subsets
0977      @return a new codec which can be used for modelling <i>subset</i>
0978      *         problems.
0979      @throws NullPointerException if the given {@code basicSet} is
0980      *         {@code null}; {@code null} elements are allowed.
0981      @throws IllegalArgumentException if {@code basicSet.size() < size},
0982      *         {@code size <= 0} or {@code basicSet.size()*size} will cause an
0983      *         integer overflow.
0984      */
0985     public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> ofSubSet(
0986         final ISeq<? extends T> basicSet,
0987         final int size
0988     ) {
0989         requireNonNull(basicSet);
0990         Combinatorics.checkSubSet(basicSet.size(), size);
0991 
0992         final Map<T, EnumGene<T>> genes =
0993             IntStream.range(0, basicSet.length())
0994                 .mapToObj(i -> EnumGene.<T>of(i, basicSet))
0995                 .collect(Collectors.toMap(EnumGene::allele, identity()));
0996 
0997         return InvertibleCodec.of(
0998             Genotype.of(PermutationChromosome.of(basicSet, size)),
0999             gt -> gt.chromosome().stream()
1000                 .map(EnumGene::allele)
1001                 .collect(ISeq.toISeq()),
1002             values -> {
1003                 if (values.size() != size) {
1004                     throw new IllegalArgumentException(format(
1005                         "Expected sub-set size of %d, but got %d,",
1006                         size, values.size()
1007                     ));
1008                 }
1009 
1010                 return Genotype.of(
1011                     new PermutationChromosome<>(
1012                         values.stream()
1013                             .map(genes::get)
1014                             .collect(ISeq.toISeq())
1015                     )
1016                 );
1017             }
1018         );
1019     }
1020 
1021 //    /**
1022 //     * Creates a codec for a 2-dimensional affine transformation. The composed
1023 //     * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
1024 //     *
1025 //     * @param sx the scale factor range in x direction
1026 //     * @param sy the scale factor range in y direction
1027 //     * @param tx the translation range in x direction
1028 //     * @param ty the translation range in y direction
1029 //     * @param th the rotation range (in radians)
1030 //     * @param kx the shear range in x direction
1031 //     * @param ky the shear range in x direction
1032 //     * @return the affine transformation codec
1033 //     * @throws NullPointerException if one of the arguments is {@code null}
1034 //     */
1035 //    static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1036 //        final DoubleRange sx, final DoubleRange sy,
1037 //        final DoubleRange tx, final DoubleRange ty,
1038 //        final DoubleRange th,
1039 //        final DoubleRange kx, final DoubleRange ky
1040 //    ) {
1041 //        return Codec.of(
1042 //            Genotype.of(
1043 //                // Scale
1044 //                DoubleChromosome.of(sx), DoubleChromosome.of(sy),
1045 //                // Translation
1046 //                DoubleChromosome.of(tx), DoubleChromosome.of(ty),
1047 //                // Rotation
1048 //                DoubleChromosome.of(th),
1049 //                // Shear
1050 //                DoubleChromosome.of(kx), DoubleChromosome.of(ky)
1051 //            ),
1052 //            gt -> {
1053 //                final AffineTransform at = new AffineTransform();
1054 //
1055 //                at.translate(
1056 //                    gt.getChromosome(2).getGene().doubleValue(),
1057 //                    gt.getChromosome(3).getGene().doubleValue()
1058 //                );
1059 //                at.shear(
1060 //                    gt.getChromosome(5).getGene().doubleValue(),
1061 //                    gt.getChromosome(6).getGene().doubleValue()
1062 //                );
1063 //                at.scale(
1064 //                    gt.getChromosome(0).getGene().doubleValue(),
1065 //                    gt.getChromosome(1).getGene().doubleValue()
1066 //                );
1067 //                at.rotate(gt.getChromosome(4).getGene().doubleValue());
1068 //
1069 //                return at;
1070 //            }
1071 //        );
1072 //    }
1073 //
1074 //    /**
1075 //     * Creates a codec for a 2-dimensional affine transformation. The composed
1076 //     * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
1077 //     *
1078 //     * @param s the scale factor range in x and y direction
1079 //     * @param t the translation range in x and y direction
1080 //     * @param th the rotation angle range
1081 //     * @param k the shear range in x and y direction
1082 //     * @return the affine transformation codec
1083 //     * @throws NullPointerException if one of the arguments is {@code null}
1084 //     */
1085 //    static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1086 //        final DoubleRange s,
1087 //        final DoubleRange t,
1088 //        final DoubleRange th,
1089 //        final DoubleRange k
1090 //    ) {
1091 //        return ofAffineTransform(s, s, t, t, th, k, k);
1092 //    }
1093 
1094 }