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•Sc•Sh•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•Sc•Sh•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 }
|