0001 /*
0002 * Java Genetic Algorithm Library (jenetics-5.2.0).
0003 * Copyright (c) 2007-2020 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,
0819 Entry::getValue,
0820 (u,v) -> {throw new IllegalStateException("Duplicate key " + u);},
0821 mapSupplier));
0822 }
0823
0824 private static <A, B> Genotype<EnumGene<Integer>> toEncoding(
0825 final Map<A, B> mapping,
0826 final Map<A, Integer> source,
0827 final Map<B, Integer> target,
0828 final Map<Integer, EnumGene<Integer>> genes
0829 ) {
0830 final int[] perm = new int[target.size()];
0831 source.forEach((key, value) -> {
0832 final int i = value;
0833 final int j = target.get(mapping.get(key));
0834 perm[i%perm.length] = j;
0835 });
0836
0837 // Fill the rest of the 'perm' array, without duplicates.
0838 // TODO: can be done more efficiently
0839 if (target.size() > source.size()) {
0840 final Set<Integer> indexes = new HashSet<>();
0841 for (int i = 0; i < target.size(); ++i) {
0842 indexes.add(i);
0843 }
0844
0845 for (int i = 0; i < source.size(); ++i) {
0846 indexes.remove(perm[i]);
0847 }
0848
0849 final Iterator<Integer> it = indexes.iterator();
0850 for (int i = source.size(); i < target.size(); ++i) {
0851 perm[i] = it.next();
0852 it.remove();
0853 }
0854 }
0855
0856 return Genotype.of(
0857 new PermutationChromosome<>(
0858 IntStream.of(perm)
0859 .mapToObj(genes::get)
0860 .collect(ISeq.toISeq())
0861 )
0862 );
0863 }
0864
0865 /**
0866 * Create a codec, which creates a a mapping from the elements given in the
0867 * {@code source} sequence to the elements given in the {@code target}
0868 * sequence. The returned mapping can be seen as a function which maps every
0869 * element of the {@code target} set to an element of the {@code source} set.
0870 *
0871 * <pre>{@code
0872 * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
0873 * final ISeq<String> strings = ISeq.of("1", "2", "3");
0874 *
0875 * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
0876 * Codecs.ofMapping(numbers, strings);
0877 * }</pre>
0878 *
0879 * If {@code source.size() > target.size()}, the created mapping is
0880 * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
0881 * if {@code source.size() < target.size()}, the mapping is
0882 * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
0883 * and if both sets have the same size, the returned mapping is
0884 * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
0885 *
0886 * @since 4.3
0887 *
0888 * @param source the source elements. Will be the <em>keys</em> of the
0889 * encoded {@code Map}.
0890 * @param target the target elements. Will be the <em>values</em> of the
0891 * encoded {@code Map}.
0892 * @param <A> the type of the source elements
0893 * @param <B> the type of the target elements
0894 * @return a new mapping codec
0895 * @throws IllegalArgumentException if the {@code target} sequences are empty
0896 * @throws NullPointerException if one of the argument is {@code null}
0897 */
0898 public static <A, B> InvertibleCodec<Map<A, B>, EnumGene<Integer>>
0899 ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
0900 return ofMapping(source, target, HashMap::new);
0901 }
0902
0903 /**
0904 * The subset {@link InvertibleCodec} can be used for problems where it is
0905 * required to find the best <b>variable-sized</b> subset from given basic
0906 * set. A typical usage example of the returned {@code Codec} is the
0907 * Knapsack problem.
0908 * <p>
0909 * The following code snippet shows a simplified variation of the Knapsack
0910 * problem.
0911 * <pre>{@code
0912 * public final class Main {
0913 * // The basic set from where to choose an 'optimal' subset.
0914 * private final static ISeq<Integer> SET =
0915 * ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
0916 *
0917 * // Fitness function directly takes an 'int' value.
0918 * private static int fitness(final ISeq<Integer> subset) {
0919 * assert(subset.size() <= SET.size());
0920 * final int size = subset.stream()
0921 * .collect(Collectors.summingInt(Integer::intValue));
0922 * return size <= 20 ? size : 0;
0923 * }
0924 *
0925 * public static void main(final String[] args) {
0926 * final Engine<BitGene, Double> engine = Engine
0927 * .builder(Main::fitness, codec.ofSubSet(SET))
0928 * .build();
0929 * ...
0930 * }
0931 * }
0932 * }</pre>
0933 *
0934 * @param <T> the element type of the basic set
0935 * @param basicSet the basic set, from where to choose the <i>optimal</i>
0936 * subset.
0937 * @return a new codec which can be used for modelling <i>subset</i>
0938 * problems.
0939 * @throws NullPointerException if the given {@code basicSet} is
0940 * {@code null}; {@code null} elements are allowed.
0941 * @throws IllegalArgumentException if the {@code basicSet} size is smaller
0942 * than one.
0943 */
0944 public static <T> InvertibleCodec<ISeq<T>, BitGene>
0945 ofSubSet(final ISeq<? extends T> basicSet) {
0946 requireNonNull(basicSet);
0947 Requires.positive(basicSet.length());
0948
0949 return InvertibleCodec.of(
0950 Genotype.of(BitChromosome.of(basicSet.length())),
0951 gt -> gt.chromosome()
0952 .as(BitChromosome.class).ones()
0953 .<T>mapToObj(basicSet)
0954 .collect(ISeq.toISeq()),
0955 values -> {
0956 final byte[] bits = Bits.newArray(basicSet.size());
0957
0958 int i = 0;
0959 for (T v : values) {
0960 while (i < basicSet.size() && !Objects.equals(basicSet.get(i), v)) {
0961 ++i;
0962 }
0963 Bits.set(bits, i);
0964 }
0965
0966 return Genotype.of(new BitChromosome(bits, 0, basicSet.size()));
0967 }
0968 );
0969 }
0970
0971 /**
0972 * The subset {@link InvertibleCodec} can be used for problems where it is
0973 * required to find the best <b>fixed-size</b> subset from given basic set.
0974 *
0975 * @since 3.4
0976 *
0977 * @see PermutationChromosome
0978 * @see PermutationChromosome#of(ISeq, int)
0979 *
0980 * @param <T> the element type of the basic set
0981 * @param basicSet the basic set, from where to choose the <i>optimal</i>
0982 * subset.
0983 * @param size the length of the desired subsets
0984 * @return a new codec which can be used for modelling <i>subset</i>
0985 * problems.
0986 * @throws NullPointerException if the given {@code basicSet} is
0987 * {@code null}; {@code null} elements are allowed.
0988 * @throws IllegalArgumentException if {@code basicSet.size() < size},
0989 * {@code size <= 0} or {@code basicSet.size()*size} will cause an
0990 * integer overflow.
0991 */
0992 public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> ofSubSet(
0993 final ISeq<? extends T> basicSet,
0994 final int size
0995 ) {
0996 requireNonNull(basicSet);
0997 Combinatorics.checkSubSet(basicSet.size(), size);
0998
0999 final Map<T, EnumGene<T>> genes =
1000 IntStream.range(0, basicSet.length())
1001 .mapToObj(i -> EnumGene.<T>of(i, basicSet))
1002 .collect(Collectors.toMap(EnumGene::allele, identity()));
1003
1004 return InvertibleCodec.of(
1005 Genotype.of(PermutationChromosome.of(basicSet, size)),
1006 gt -> gt.chromosome().stream()
1007 .map(EnumGene::allele)
1008 .collect(ISeq.toISeq()),
1009 values -> {
1010 if (values.size() != size) {
1011 throw new IllegalArgumentException(format(
1012 "Expected sub-set size of %d, but got %d,",
1013 size, values.size()
1014 ));
1015 }
1016
1017 return Genotype.of(
1018 new PermutationChromosome<>(
1019 values.stream()
1020 .map(genes::get)
1021 .collect(ISeq.toISeq())
1022 )
1023 );
1024 }
1025 );
1026 }
1027
1028 // /**
1029 // * Creates a codec for a 2-dimensional affine transformation. The composed
1030 // * order of the transformation is: <i>R•Sc•Sh•T</i>
1031 // *
1032 // * @param sx the scale factor range in x direction
1033 // * @param sy the scale factor range in y direction
1034 // * @param tx the translation range in x direction
1035 // * @param ty the translation range in y direction
1036 // * @param th the rotation range (in radians)
1037 // * @param kx the shear range in x direction
1038 // * @param ky the shear range in x direction
1039 // * @return the affine transformation codec
1040 // * @throws NullPointerException if one of the arguments is {@code null}
1041 // */
1042 // static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1043 // final DoubleRange sx, final DoubleRange sy,
1044 // final DoubleRange tx, final DoubleRange ty,
1045 // final DoubleRange th,
1046 // final DoubleRange kx, final DoubleRange ky
1047 // ) {
1048 // return Codec.of(
1049 // Genotype.of(
1050 // // Scale
1051 // DoubleChromosome.of(sx), DoubleChromosome.of(sy),
1052 // // Translation
1053 // DoubleChromosome.of(tx), DoubleChromosome.of(ty),
1054 // // Rotation
1055 // DoubleChromosome.of(th),
1056 // // Shear
1057 // DoubleChromosome.of(kx), DoubleChromosome.of(ky)
1058 // ),
1059 // gt -> {
1060 // final AffineTransform at = new AffineTransform();
1061 //
1062 // at.translate(
1063 // gt.getChromosome(2).getGene().doubleValue(),
1064 // gt.getChromosome(3).getGene().doubleValue()
1065 // );
1066 // at.shear(
1067 // gt.getChromosome(5).getGene().doubleValue(),
1068 // gt.getChromosome(6).getGene().doubleValue()
1069 // );
1070 // at.scale(
1071 // gt.getChromosome(0).getGene().doubleValue(),
1072 // gt.getChromosome(1).getGene().doubleValue()
1073 // );
1074 // at.rotate(gt.getChromosome(4).getGene().doubleValue());
1075 //
1076 // return at;
1077 // }
1078 // );
1079 // }
1080 //
1081 // /**
1082 // * Creates a codec for a 2-dimensional affine transformation. The composed
1083 // * order of the transformation is: <i>R•Sc•Sh•T</i>
1084 // *
1085 // * @param s the scale factor range in x and y direction
1086 // * @param t the translation range in x and y direction
1087 // * @param th the rotation angle range
1088 // * @param k the shear range in x and y direction
1089 // * @return the affine transformation codec
1090 // * @throws NullPointerException if one of the arguments is {@code null}
1091 // */
1092 // static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1093 // final DoubleRange s,
1094 // final DoubleRange t,
1095 // final DoubleRange th,
1096 // final DoubleRange k
1097 // ) {
1098 // return ofAffineTransform(s, s, t, t, th, k, k);
1099 // }
1100
1101 }
|