001 /*
002 * Java Genetic Algorithm Library (jenetics-4.4.0).
003 * Copyright (c) 2007-2019 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020 package io.jenetics.engine;
021
022 import static java.lang.reflect.Array.newInstance;
023 import static java.util.Objects.requireNonNull;
024
025 import java.util.AbstractMap.SimpleImmutableEntry;
026 import java.util.HashMap;
027 import java.util.Map;
028 import java.util.Map.Entry;
029 import java.util.Objects;
030 import java.util.function.Predicate;
031 import java.util.function.Supplier;
032 import java.util.stream.Collectors;
033 import java.util.stream.IntStream;
034 import java.util.stream.Stream;
035
036 import io.jenetics.AnyChromosome;
037 import io.jenetics.AnyGene;
038 import io.jenetics.BitChromosome;
039 import io.jenetics.BitGene;
040 import io.jenetics.DoubleChromosome;
041 import io.jenetics.DoubleGene;
042 import io.jenetics.EnumGene;
043 import io.jenetics.Gene;
044 import io.jenetics.Genotype;
045 import io.jenetics.IntegerChromosome;
046 import io.jenetics.IntegerGene;
047 import io.jenetics.LongChromosome;
048 import io.jenetics.LongGene;
049 import io.jenetics.PermutationChromosome;
050 import io.jenetics.internal.math.comb;
051 import io.jenetics.internal.util.Predicates;
052 import io.jenetics.internal.util.require;
053 import io.jenetics.util.DoubleRange;
054 import io.jenetics.util.ISeq;
055 import io.jenetics.util.IntRange;
056 import io.jenetics.util.LongRange;
057
058 /**
059 * This class contains factory methods for creating common problem encodings.
060 *
061 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
062 * @since 3.2
063 * @version 4.4
064 */
065 public final class Codecs {
066
067 private Codecs() {}
068
069 /**
070 * Return a scalar {@code Codec} for the given range.
071 *
072 * @param domain the domain of the returned {@code Codec}
073 * @return a new scalar {@code Codec} with the given domain.
074 * @throws NullPointerException if the given {@code domain} is {@code null}
075 */
076 public static Codec<Integer, IntegerGene> ofScalar(final IntRange domain) {
077 requireNonNull(domain);
078
079 return Codec.of(
080 Genotype.of(IntegerChromosome.of(domain)),
081 gt -> gt.getChromosome().getGene().getAllele()
082 );
083 }
084
085 /**
086 * Return a scalar {@code Codec} for the given range.
087 *
088 * @param domain the domain of the returned {@code Codec}
089 * @return a new scalar {@code Codec} with the given domain.
090 * @throws NullPointerException if the given {@code domain} is {@code null}
091 */
092 public static Codec<Long, LongGene> ofScalar(final LongRange domain) {
093 requireNonNull(domain);
094
095 return Codec.of(
096 Genotype.of(LongChromosome.of(domain)),
097 gt -> gt.getGene().getAllele()
098 );
099 }
100
101 /**
102 * Return a scalar {@code Codec} for the given range.
103 *
104 * @param domain the domain of the returned {@code Codec}
105 * @return a new scalar {@code Codec} with the given domain.
106 * @throws NullPointerException if the given {@code domain} is {@code null}
107 */
108 public static Codec<Double, DoubleGene> ofScalar(final DoubleRange domain) {
109 requireNonNull(domain);
110
111 return Codec.of(
112 Genotype.of(DoubleChromosome.of(domain)),
113 gt -> gt.getGene().getAllele()
114 );
115 }
116
117 /**
118 * Return a scala {@code Codec} with the given allele {@link Supplier} and
119 * allele {@code validator}. The {@code supplier} is responsible for
120 * creating new random alleles, and the {@code validator} can verify it.
121 * <p>
122 * The following example shows a codec which creates and verifies
123 * {@code BigInteger} objects.
124 * <pre>{@code
125 * final Codec<BigInteger, AnyGene<BigInteger>> codec = Codecs.of(
126 * // Create new random 'BigInteger' object.
127 * () -> {
128 * final byte[] data = new byte[100];
129 * RandomRegistry.getRandom().nextBytes(data);
130 * return new BigInteger(data);
131 * },
132 * // Verify that bit 7 is set. (For illustration purpose.)
133 * bi -> bi.testBit(7)
134 * );
135 * }</pre>
136 *
137 * @see AnyGene#of(Supplier, Predicate)
138 * @see AnyChromosome#of(Supplier, Predicate)
139 *
140 * @param <A> the allele type
141 * @param supplier the allele-supplier which is used for creating new,
142 * random alleles
143 * @param validator the validator used for validating the created gene. This
144 * predicate is used in the {@link AnyGene#isValid()} method.
145 * @return a new {@code Codec} with the given parameters
146 * @throws NullPointerException if one of the parameters is {@code null}
147 */
148 public static <A> Codec<A, AnyGene<A>> ofScalar(
149 final Supplier<? extends A> supplier,
150 final Predicate<? super A> validator
151 ) {
152 return Codec.of(
153 Genotype.of(AnyChromosome.of(supplier, validator)),
154 gt -> gt.getGene().getAllele()
155 );
156 }
157
158 /**
159 * Return a scala {@code Codec} with the given allele {@link Supplier} and
160 * allele {@code validator}. The {@code supplier} is responsible for
161 * creating new random alleles.
162 *
163 * @see #ofScalar(Supplier, Predicate)
164 * @see AnyGene#of(Supplier)
165 * @see AnyChromosome#of(Supplier)
166 *
167 * @param <A> the allele type
168 * @param supplier the allele-supplier which is used for creating new,
169 * random alleles
170 * @return a new {@code Codec} with the given parameters
171 * @throws NullPointerException if the parameter is {@code null}
172 */
173 public static <A> Codec<A, AnyGene<A>> ofScalar(
174 final Supplier<? extends A> supplier
175 ) {
176 return Codec.of(
177 Genotype.of(AnyChromosome.of(supplier)),
178 gt -> gt.getGene().getAllele()
179 );
180 }
181
182 /**
183 * Return a vector {@code Codec} for the given range. All vector values
184 * are restricted by the same domain.
185 *
186 * @param domain the domain of the vector values
187 * @param length the vector length
188 * @return a new vector {@code Codec}
189 * @throws NullPointerException if the given {@code domain} is {@code null}
190 * @throws IllegalArgumentException if the {@code length} is smaller than
191 * one.
192 */
193 public static Codec<int[], IntegerGene> ofVector(
194 final IntRange domain,
195 final int length
196 ) {
197 requireNonNull(domain);
198 require.positive(length);
199
200 return Codec.of(
201 Genotype.of(IntegerChromosome.of(domain, length)),
202 gt -> gt.getChromosome().as(IntegerChromosome.class).toArray()
203 );
204 }
205
206 /**
207 * Return a vector {@code Codec} for the given range. All vector values
208 * are restricted by the same domain.
209 *
210 * @param domain the domain of the vector values
211 * @param length the vector length
212 * @return a new vector {@code Codec}
213 * @throws NullPointerException if the given {@code domain} is {@code null}
214 * @throws IllegalArgumentException if the {@code length} is smaller than
215 * one.
216 */
217 public static Codec<long[], LongGene> ofVector(
218 final LongRange domain,
219 final int length
220 ) {
221 requireNonNull(domain);
222 require.positive(length);
223
224 return Codec.of(
225 Genotype.of(LongChromosome.of(domain, length)),
226 gt -> gt.getChromosome().as(LongChromosome.class).toArray()
227 );
228 }
229
230 /**
231 * Return a vector {@code Codec} for the given range. All vector values
232 * are restricted by the same domain.
233 *
234 * @param domain the domain of the vector values
235 * @param length the vector length
236 * @return a new vector {@code Codec}
237 * @throws NullPointerException if the given {@code domain} is {@code null}
238 * @throws IllegalArgumentException if the {@code length} is smaller than
239 * one.
240 */
241 public static Codec<double[], DoubleGene> ofVector(
242 final DoubleRange domain,
243 final int length
244 ) {
245 requireNonNull(domain);
246 require.positive(length);
247
248 return Codec.of(
249 Genotype.of(DoubleChromosome.of(domain, length)),
250 gt -> gt.getChromosome().as(DoubleChromosome.class).toArray()
251 );
252 }
253
254 /**
255 * Create a vector {@code Codec} for the given ranges. Each vector element
256 * might have a different domain. The vector length is equal to the number
257 * of domains.
258 *
259 * @param domains the domain ranges
260 * @return a new vector {@code Codec}
261 * @throws NullPointerException if one of the arguments is {@code null}
262 * @throws IllegalArgumentException if the {@code domains} array is empty
263 */
264 public static Codec<int[], IntegerGene> ofVector(final IntRange... domains) {
265 if (domains.length == 0) {
266 throw new IllegalArgumentException("Domains must not be empty.");
267 }
268
269 final ISeq<IntegerChromosome> chromosomes = Stream.of(domains)
270 .peek(Objects::requireNonNull)
271 .map(IntegerGene::of)
272 .map(IntegerChromosome::of)
273 .collect(ISeq.toISeq());
274
275 return Codec.of(
276 Genotype.of(chromosomes),
277 gt -> {
278 final int[] args = new int[gt.length()];
279 for (int i = gt.length(); --i >= 0;) {
280 args[i] = gt.getChromosome(i).getGene().intValue();
281 }
282 return args;
283 }
284 );
285 }
286
287 /**
288 * Create a vector {@code Codec} for the given ranges. Each vector element
289 * might have a different domain. The vector length is equal to the number
290 * of domains.
291 *
292 * @param domains the domain ranges
293 * @return a new vector {@code Codec}
294 * @throws NullPointerException if one of the arguments is {@code null}
295 * @throws IllegalArgumentException if the {@code domains} array is empty
296 */
297 public static Codec<long[], LongGene> ofVector(final LongRange... domains) {
298 if (domains.length == 0) {
299 throw new IllegalArgumentException("Domains must not be empty.");
300 }
301
302 final ISeq<LongChromosome> chromosomes = Stream.of(domains)
303 .peek(Objects::requireNonNull)
304 .map(LongGene::of)
305 .map(LongChromosome::of)
306 .collect(ISeq.toISeq());
307
308 return Codec.of(
309 Genotype.of(chromosomes),
310 gt -> {
311 final long[] args = new long[gt.length()];
312 for (int i = gt.length(); --i >= 0;) {
313 args[i] = gt.getChromosome(i).getGene().longValue();
314 }
315 return args;
316 }
317 );
318 }
319
320 /**
321 * Create a vector {@code Codec} for the given ranges. Each vector element
322 * might have a different domain. The vector length is equal to the number
323 * of domains.
324 *
325 * @param domains the domain ranges
326 * @return a new vector {@code Codec}
327 * @throws NullPointerException if one of the arguments is {@code null}
328 * @throws IllegalArgumentException if the {@code domains} array is empty
329 */
330 public static Codec<double[], DoubleGene> ofVector(
331 final DoubleRange... domains
332 ) {
333 if (domains.length == 0) {
334 throw new IllegalArgumentException("Domains must not be empty.");
335 }
336
337 final ISeq<DoubleChromosome> chromosomes = Stream.of(domains)
338 .peek(Objects::requireNonNull)
339 .map(DoubleGene::of)
340 .map(DoubleChromosome::of)
341 .collect(ISeq.toISeq());
342
343 return Codec.of(
344 Genotype.of(chromosomes),
345 gt -> {
346 final double[] args = new double[gt.length()];
347 for (int i = gt.length(); --i >= 0;) {
348 args[i] = gt.getChromosome(i).getGene().doubleValue();
349 }
350 return args;
351 }
352 );
353 }
354
355 /**
356 * Return a scala {@code Codec} with the given allele {@link Supplier},
357 * allele {@code validator} and {@code Chromosome} length. The
358 * {@code supplier} is responsible for creating new random alleles, and the
359 * {@code validator} can verify it.
360 * <p>
361 * The following example shows a codec which creates and verifies
362 * {@code BigInteger} object arrays.
363 * <pre>{@code
364 * final Codec<BigInteger[], AnyGene<BigInteger>> codec = Codecs.of(
365 * // Create new random 'BigInteger' object.
366 * () -> {
367 * final byte[] data = new byte[100];
368 * RandomRegistry.getRandom().nextBytes(data);
369 * return new BigInteger(data);
370 * },
371 * // Verify that bit 7 is set. (For illustration purpose.)
372 * bi -> bi.testBit(7),
373 * // The 'Chromosome' length.
374 * 123
375 * );
376 * }</pre>
377 *
378 * @see AnyChromosome#of(Supplier, Predicate, Predicate, int)
379 *
380 * @param <A> the allele type
381 * @param supplier the allele-supplier which is used for creating new,
382 * random alleles
383 * @param alleleValidator the validator used for validating the created gene.
384 * This predicate is used in the {@link AnyGene#isValid()} method.
385 * @param alleleSeqValidator the validator used for validating the created
386 * chromosome. This predicate is used in the
387 * {@link AnyChromosome#isValid()} method.
388 * @param length the vector length
389 * @return a new {@code Codec} with the given parameters
390 * @throws NullPointerException if one of the parameters is {@code null}
391 * @throws IllegalArgumentException if the length of the vector is smaller
392 * than one.
393 */
394 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
395 final Supplier<? extends A> supplier,
396 final Predicate<? super A> alleleValidator,
397 final Predicate<? super ISeq<A>> alleleSeqValidator,
398 final int length
399 ) {
400 requireNonNull(supplier);
401 requireNonNull(alleleSeqValidator);
402 requireNonNull(alleleSeqValidator);
403 require.positive(length);
404
405 return Codec.of(
406 Genotype.of(AnyChromosome
407 .of(supplier, alleleValidator, alleleSeqValidator, length)),
408 gt -> gt.getChromosome().stream()
409 .map(Gene::getAllele)
410 .collect(ISeq.toISeq())
411 );
412 }
413
414 /**
415 * Return a scala {@code Codec} with the given allele {@link Supplier},
416 * allele {@code validator} and {@code Chromosome} length. The
417 * {@code supplier} is responsible for creating new random alleles, and the
418 * {@code validator} can verify it.
419 *
420 * @param <A> the allele type
421 * @param supplier the allele-supplier which is used for creating new,
422 * random alleles
423 * @param validator the validator used for validating the created gene. This
424 * predicate is used in the {@link AnyGene#isValid()} method.
425 * @param length the vector length
426 * @return a new {@code Codec} with the given parameters
427 * @throws NullPointerException if one of the parameters is {@code null}
428 * @throws IllegalArgumentException if the length of the vector is smaller
429 * than one.
430 */
431 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
432 final Supplier<? extends A> supplier,
433 final Predicate<? super A> validator,
434 final int length
435 ) {
436 return ofVector(
437 supplier,
438 validator,
439 Predicates.<ISeq<A>>True(),
440 length
441 );
442 }
443
444 /**
445 * Return a scala {@code Codec} with the given allele {@link Supplier} and
446 * {@code Chromosome} length. The {@code supplier} is responsible for
447 * creating new random alleles.
448 *
449 * @param <A> the allele type
450 * @param supplier the allele-supplier which is used for creating new,
451 * random alleles
452 * @param length the vector length
453 * @return a new {@code Codec} with the given parameters
454 * @throws NullPointerException if one of the parameters is {@code null}
455 * @throws IllegalArgumentException if the length of the vector is smaller
456 * than one.
457 */
458 public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
459 final Supplier<? extends A> supplier,
460 final int length
461 ) {
462 return ofVector(supplier, Predicates.TRUE, length);
463 }
464
465 /**
466 * Create a permutation {@code Codec} of integer in the range
467 * {@code [0, length)}.
468 *
469 * @param length the number of permutation elements
470 * @return a permutation {@code Codec} of integers
471 * @throws IllegalArgumentException if the {@code length} is smaller than
472 * one.
473 */
474 public static Codec<int[], EnumGene<Integer>> ofPermutation(final int length) {
475 require.positive(length);
476
477 return Codec.of(
478 Genotype.of(PermutationChromosome.ofInteger(length)),
479 gt -> gt.getChromosome().stream()
480 .mapToInt(EnumGene::getAllele)
481 .toArray()
482 );
483 }
484
485 @SuppressWarnings("unchecked")
486 private static <T> T[] newArray(final Class<?> type, final int length) {
487 return (T[])newInstance(type, length);
488 }
489
490 /**
491 * Return a 2-dimensional matrix {@code Codec} for the given range. All
492 * matrix values are restricted by the same domain. The dimension of the
493 * returned matrix is {@code int[rows][cols]}.
494 *
495 * @since 4.4
496 *
497 * @param domain the domain of the matrix values
498 * @param rows the number of rows of the matrix
499 * @param cols the number of columns of the matrix
500 * @return a new matrix {@code Codec}
501 * @throws NullPointerException if the given {@code domain} is {@code null}
502 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
503 * smaller than one.
504 */
505 public static Codec<int[][], IntegerGene> ofMatrix(
506 final IntRange domain,
507 final int rows,
508 final int cols
509 ) {
510 requireNonNull(domain);
511 require.positive(rows);
512 require.positive(cols);
513
514 return Codec.of(
515 Genotype.of(
516 IntegerChromosome.of(domain, cols).instances()
517 .limit(rows)
518 .collect(ISeq.toISeq())
519 ),
520 gt -> gt.stream()
521 .map(ch -> ch.stream()
522 .mapToInt(IntegerGene::intValue)
523 .toArray())
524 .toArray(int[][]::new)
525 );
526 }
527
528 /**
529 * Return a 2-dimensional matrix {@code Codec} for the given range. All
530 * matrix values are restricted by the same domain. The dimension of the
531 * returned matrix is {@code long[rows][cols]}.
532 *
533 * @since 4.4
534 *
535 * @param domain the domain of the matrix values
536 * @param rows the number of rows of the matrix
537 * @param cols the number of columns of the matrix
538 * @return a new matrix {@code Codec}
539 * @throws NullPointerException if the given {@code domain} is {@code null}
540 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
541 * smaller than one.
542 */
543 public static Codec<long[][], LongGene> ofMatrix(
544 final LongRange domain,
545 final int rows,
546 final int cols
547 ) {
548 requireNonNull(domain);
549 require.positive(rows);
550 require.positive(cols);
551
552 return Codec.of(
553 Genotype.of(
554 LongChromosome.of(domain, cols).instances()
555 .limit(rows)
556 .collect(ISeq.toISeq())
557 ),
558 gt -> gt.stream()
559 .map(ch -> ch.stream()
560 .mapToLong(LongGene::longValue)
561 .toArray())
562 .toArray(long[][]::new)
563 );
564 }
565
566 /**
567 * Return a 2-dimensional matrix {@code Codec} for the given range. All
568 * matrix values are restricted by the same domain. The dimension of the
569 * returned matrix is {@code double[rows][cols]}.
570 *
571 * @since 4.4
572 *
573 * @param domain the domain of the matrix values
574 * @param rows the number of rows of the matrix
575 * @param cols the number of columns of the matrix
576 * @return a new matrix {@code Codec}
577 * @throws NullPointerException if the given {@code domain} is {@code null}
578 * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
579 * smaller than one.
580 */
581 public static Codec<double[][], DoubleGene> ofMatrix(
582 final DoubleRange domain,
583 final int rows,
584 final int cols
585 ) {
586 requireNonNull(domain);
587 require.positive(rows);
588 require.positive(cols);
589
590 return Codec.of(
591 Genotype.of(
592 DoubleChromosome.of(domain, cols).instances()
593 .limit(rows)
594 .collect(ISeq.toISeq())
595 ),
596 gt -> gt.stream()
597 .map(ch -> ch.stream()
598 .mapToDouble(DoubleGene::doubleValue)
599 .toArray())
600 .toArray(double[][]::new)
601 );
602 }
603
604 /**
605 * Create a permutation {@code Codec} with the given alleles.
606 *
607 * @param alleles the alleles of the permutation
608 * @param <T> the allele type
609 * @return a new permutation {@code Codec}
610 * @throws IllegalArgumentException if the given allele array is empty
611 * @throws NullPointerException if one of the alleles is {@code null}
612 */
613 public static <T> Codec<ISeq<T>, EnumGene<T>>
614 ofPermutation(final ISeq<? extends T> alleles) {
615 if (alleles.isEmpty()) {
616 throw new IllegalArgumentException(
617 "Empty allele array is not allowed."
618 );
619 }
620
621 return Codec.of(
622 Genotype.of(PermutationChromosome.of(alleles)),
623 gt -> gt.getChromosome().stream()
624 .map(EnumGene::getAllele)
625 .collect(ISeq.toISeq())
626 );
627 }
628
629 /**
630 * Create a codec, which creates a a mapping from the elements given in the
631 * {@code source} sequence to the elements given in the {@code target}
632 * sequence. The returned mapping can be seen as a function which maps every
633 * element of the {@code target} set to an element of the {@code source} set.
634 *
635 * <pre>{@code
636 * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
637 * final ISeq<String> strings = ISeq.of("1", "2", "3");
638 *
639 * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
640 * Codecs.ofMapping(numbers, strings, HashMap::new);
641 * }</pre>
642 *
643 * If {@code source.size() > target.size()}, the created mapping is
644 * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
645 * if {@code source.size() < target.size()}, the mapping is
646 * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
647 * and if both sets have the same size, the returned mapping is
648 * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
649 *
650 * @since 4.3
651 *
652 * @param source the source elements. Will be the <em>keys</em> of the
653 * encoded {@code Map}.
654 * @param target the target elements. Will be the <em>values</em> of the
655 * encoded {@code Map}.
656 * @param mapSupplier a function which returns a new, empty Map into which
657 * the mapping will be inserted
658 * @param <A> the type of the source elements
659 * @param <B> the type of the target elements
660 * @param <M> the type of the encoded Map
661 * @return a new mapping codec
662 * @throws IllegalArgumentException if the {@code target} sequences are empty
663 * @throws NullPointerException if one of the argument is {@code null}
664 */
665 public static <A, B, M extends Map<A, B>> Codec<M, EnumGene<Integer>>
666 ofMapping(
667 final ISeq<? extends A> source,
668 final ISeq<? extends B> target,
669 final Supplier<M> mapSupplier
670 ) {
671 requireNonNull(mapSupplier);
672 return ofPermutation(target.size())
673 .map(perm -> toMapping(perm, source, target, mapSupplier));
674 }
675
676 private static <A, B, M extends Map<A, B>> M toMapping(
677 final int[] perm,
678 final ISeq<? extends A> source,
679 final ISeq<? extends B> target,
680 final Supplier<M> mapSupplier
681 ) {
682 return IntStream.range(0, source.size())
683 .mapToObj(i -> new SimpleImmutableEntry<>(
684 source.get(i), target.get(perm[i%perm.length])))
685 .collect(Collectors.toMap(
686 Entry::getKey,
687 Entry::getValue,
688 (u,v) -> {throw new IllegalStateException("Duplicate key " + u);},
689 mapSupplier));
690 }
691
692 /**
693 * Create a codec, which creates a a mapping from the elements given in the
694 * {@code source} sequence to the elements given in the {@code target}
695 * sequence. The returned mapping can be seen as a function which maps every
696 * element of the {@code target} set to an element of the {@code source} set.
697 *
698 * <pre>{@code
699 * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
700 * final ISeq<String> strings = ISeq.of("1", "2", "3");
701 *
702 * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
703 * Codecs.ofMapping(numbers, strings);
704 * }</pre>
705 *
706 * If {@code source.size() > target.size()}, the created mapping is
707 * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
708 * if {@code source.size() < target.size()}, the mapping is
709 * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
710 * and if both sets have the same size, the returned mapping is
711 * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
712 *
713 * @since 4.3
714 *
715 * @param source the source elements. Will be the <em>keys</em> of the
716 * encoded {@code Map}.
717 * @param target the target elements. Will be the <em>values</em> of the
718 * encoded {@code Map}.
719 * @param <A> the type of the source elements
720 * @param <B> the type of the target elements
721 * @return a new mapping codec
722 * @throws IllegalArgumentException if the {@code target} sequences are empty
723 * @throws NullPointerException if one of the argument is {@code null}
724 */
725 public static <A, B> Codec<Map<A, B>, EnumGene<Integer>>
726 ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
727 return ofMapping(source, target, HashMap::new);
728 }
729
730 /**
731 * The subset {@code Codec} can be used for problems where it is required to
732 * find the best <b>variable-sized</b> subset from given basic set. A typical
733 * usage example of the returned {@code Codec} is the Knapsack problem.
734 * <p>
735 * The following code snippet shows a simplified variation of the Knapsack
736 * problem.
737 * <pre>{@code
738 * public final class Main {
739 * // The basic set from where to choose an 'optimal' subset.
740 * private final static ISeq<Integer> SET =
741 * ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
742 *
743 * // Fitness function directly takes an 'int' value.
744 * private static int fitness(final ISeq<Integer> subset) {
745 * assert(subset.size() <= SET.size());
746 * final int size = subset.stream()
747 * .collect(Collectors.summingInt(Integer::intValue));
748 * return size <= 20 ? size : 0;
749 * }
750 *
751 * public static void main(final String[] args) {
752 * final Engine<BitGene, Double> engine = Engine
753 * .builder(Main::fitness, codec.ofSubSet(SET))
754 * .build();
755 * ...
756 * }
757 * }
758 * }</pre>
759 *
760 * @param <T> the element type of the basic set
761 * @param basicSet the basic set, from where to choose the <i>optimal</i>
762 * subset.
763 * @return a new codec which can be used for modelling <i>subset</i>
764 * problems.
765 * @throws NullPointerException if the given {@code basicSet} is
766 * {@code null}; {@code null} elements are allowed.
767 * @throws IllegalArgumentException if the {@code basicSet} size is smaller
768 * than one.
769 */
770 public static <T> Codec<ISeq<T>, BitGene> ofSubSet(
771 final ISeq<? extends T> basicSet
772 ) {
773 requireNonNull(basicSet);
774 require.positive(basicSet.length());
775
776 return Codec.of(
777 Genotype.of(BitChromosome.of(basicSet.length())),
778 gt -> gt.getChromosome()
779 .as(BitChromosome.class).ones()
780 .<T>mapToObj(basicSet)
781 .collect(ISeq.toISeq())
782 );
783 }
784
785 /**
786 * The subset {@code Codec} can be used for problems where it is required to
787 * find the best <b>fixed-size</b> subset from given basic set.
788 *
789 * @since 3.4
790 *
791 * @see PermutationChromosome
792 * @see PermutationChromosome#of(ISeq, int)
793 *
794 * @param <T> the element type of the basic set
795 * @param basicSet the basic set, from where to choose the <i>optimal</i>
796 * subset.
797 * @param size the length of the desired subsets
798 * @return a new codec which can be used for modelling <i>subset</i>
799 * problems.
800 * @throws NullPointerException if the given {@code basicSet} is
801 * {@code null}; {@code null} elements are allowed.
802 * @throws IllegalArgumentException if {@code basicSet.size() < size},
803 * {@code size <= 0} or {@code basicSet.size()*size} will cause an
804 * integer overflow.
805 */
806 public static <T> Codec<ISeq<T>, EnumGene<T>> ofSubSet(
807 final ISeq<? extends T> basicSet,
808 final int size
809 ) {
810 requireNonNull(basicSet);
811 comb.checkSubSet(basicSet.size(), size);
812
813 return Codec.of(
814 Genotype.of(PermutationChromosome.of(basicSet, size)),
815 gt -> gt.getChromosome().stream()
816 .map(EnumGene::getAllele)
817 .collect(ISeq.toISeq())
818 );
819 }
820
821 // /**
822 // * Creates a codec for a 2-dimensional affine transformation. The composed
823 // * order of the transformation is: <i>R•Sc•Sh•T</i>
824 // *
825 // * @param sx the scale factor range in x direction
826 // * @param sy the scale factor range in y direction
827 // * @param tx the translation range in x direction
828 // * @param ty the translation range in y direction
829 // * @param th the rotation range (in radians)
830 // * @param kx the shear range in x direction
831 // * @param ky the shear range in x direction
832 // * @return the affine transformation codec
833 // * @throws NullPointerException if one of the arguments is {@code null}
834 // */
835 // static Codec<AffineTransform, DoubleGene> ofAffineTransform(
836 // final DoubleRange sx, final DoubleRange sy,
837 // final DoubleRange tx, final DoubleRange ty,
838 // final DoubleRange th,
839 // final DoubleRange kx, final DoubleRange ky
840 // ) {
841 // return Codec.of(
842 // Genotype.of(
843 // // Scale
844 // DoubleChromosome.of(sx), DoubleChromosome.of(sy),
845 // // Translation
846 // DoubleChromosome.of(tx), DoubleChromosome.of(ty),
847 // // Rotation
848 // DoubleChromosome.of(th),
849 // // Shear
850 // DoubleChromosome.of(kx), DoubleChromosome.of(ky)
851 // ),
852 // gt -> {
853 // final AffineTransform at = new AffineTransform();
854 //
855 // at.translate(
856 // gt.getChromosome(2).getGene().doubleValue(),
857 // gt.getChromosome(3).getGene().doubleValue()
858 // );
859 // at.shear(
860 // gt.getChromosome(5).getGene().doubleValue(),
861 // gt.getChromosome(6).getGene().doubleValue()
862 // );
863 // at.scale(
864 // gt.getChromosome(0).getGene().doubleValue(),
865 // gt.getChromosome(1).getGene().doubleValue()
866 // );
867 // at.rotate(gt.getChromosome(4).getGene().doubleValue());
868 //
869 // return at;
870 // }
871 // );
872 // }
873 //
874 // /**
875 // * Creates a codec for a 2-dimensional affine transformation. The composed
876 // * order of the transformation is: <i>R•Sc•Sh•T</i>
877 // *
878 // * @param s the scale factor range in x and y direction
879 // * @param t the translation range in x and y direction
880 // * @param th the rotation angle range
881 // * @param k the shear range in x and y direction
882 // * @return the affine transformation codec
883 // * @throws NullPointerException if one of the arguments is {@code null}
884 // */
885 // static Codec<AffineTransform, DoubleGene> ofAffineTransform(
886 // final DoubleRange s,
887 // final DoubleRange t,
888 // final DoubleRange th,
889 // final DoubleRange k
890 // ) {
891 // return ofAffineTransform(s, s, t, t, th, k, k);
892 // }
893
894 }
|