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