InvertibleCodec.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-8.0.0).
003  * Copyright (c) 2007-2024 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.util.Objects.requireNonNull;
023 
024 import java.util.function.Function;
025 import java.util.function.Predicate;
026 
027 import io.jenetics.Gene;
028 import io.jenetics.Genotype;
029 import io.jenetics.util.Factory;
030 
031 /**
032  * This interface extends the {@link Codec} and allows to encode an object from
033  * the problem space to a corresponding {@link Genotype}, which is the
034  <em>inverse</em> functionality of the codec. The following example shows the
035  * relation between <em>encoder</em> and <em>decoder</em> function must fulfill.
036  *
037  * {@snippet lang="java":
038  * final InvertibleCodec<int[], IntegerGene> codec =
039  *     Codecs.ofVector(IntRange.of(0, 100), 6);
040  * final int[] value = new int[]{3, 4, 6, 7, 8, 3};
041  * final Genotype<IntegerGene> gt = codec.encode(value);
042  * assert Arrays.equals(value, codec.decode(gt));
043  * }
044  *
045  * The main usage of an invertible codec is to simplify the definition of
046  {@link Constraint} objects. Instead of working with the GA classes
047  * ({@link io.jenetics.Phenotype} or {@link Genotype}), it is possible to work
048  * in the <em>native</em> problem domain {@code T}.
049  *
050  @see Constraint#of(InvertibleCodec, Predicate, Function)
051  @see RetryConstraint#of(InvertibleCodec, Predicate, Function)
052  @see RetryConstraint#of(InvertibleCodec, Predicate, int)
053  *
054  @param <T> the argument type of given problem
055  @param <G> the {@code Gene} type used for encoding the argument type {@code T}
056  *
057  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
058  @version 5.2
059  @since 5.2
060  */
061 public interface InvertibleCodec<T, G extends Gene<?, G>> extends Codec<T, G> {
062 
063     /**
064      * Return the <em>encoder</em> function which transforms a value from the
065      <em>native</em> problem domain back to the genotype. This is the
066      <em>inverse</em> of the {@link #decoder()} function. The following code
067      * snippet shows how a given value in the <em>native</em> problem domain
068      * can be converted into a {@link Genotype} and transformed back.
069      * {@snippet lang="java":
070      * final InvertibleCodec<int[], IntegerGene> codec =
071      *     Codecs.ofVector(IntRange.of(0, 100), 6);
072      * final int[] value = new int[]{3, 4, 6, 7, 8, 3};
073      * final Genotype<IntegerGene> gt = codec.encode(value);
074      * assert Arrays.equals(value, codec.decode(gt));
075      * }
076      *
077      @see #decoder()
078      @see #encode(Object)
079      *
080      @return value encoder function
081      */
082     Function<T, Genotype<G>> encoder();
083 
084     /**
085      * Decodes the given {@code value}, which is an element of the <em>native</em>
086      * problem domain, into a {@link Genotype}.
087      *
088      @param value the value of the <em>native</em> problem domain
089      @return the genotype, which represents the given {@code value}
090      */
091     default Genotype<G> encode(final T value) {
092         return encoder().apply(value);
093     }
094 
095     /**
096      * Create a new {@code InvertibleCodec} with the mapped result type.
097      *
098      * This method can also be used for creating non-trivial codes like split
099      * ranges, as shown in the following example, where only values between
100      <em>[0, 2)</em> and <em>[8, 10)</em> are valid.
101      <pre>{@code
102      *   +--+--+--+--+--+--+--+--+--+--+
103      *   |  |  |  |  |  |  |  |  |  |  |
104      *   0  1  2  3  4  5  6  7  8  9  10
105      *   |-----|xxxxxxxxxxxxxxxxx|-----|
106      *      ^  |llllllll|rrrrrrrr|  ^
107      *      |       |        |      |
108      *      +-------+        +------+
109      * }</pre>
110      *
111      * {@snippet lang="java":
112      * final InvertibleCodec<Double, DoubleGene> codec = Codecs
113      *     .ofScalar(DoubleRange.of(0, 10))
114      *     .map(v -> {
115      *             if (v >= 2 && v < 8) {
116      *                 return v < 5 ? ((v - 2)/3)*2 : ((8 - v)/3)*2 + 8;
117      *             }
118      *             return v;
119      *         },
120      *         Function.identity());
121      * }
122      *
123      @see Codec#map(Function)
124      *
125      @param mapper the mapper function
126      @param inverseMapper the inverse function of the {@code mapper}
127      @param <B> the new argument type of the given problem
128      @return a new {@link InvertibleCodec} with the mapped result type
129      @throws NullPointerException if one the mapper is {@code null}.
130      */
131     default <B>
132     InvertibleCodec<B, G> map(
133         final Function<? super T, ? extends B> mapper,
134         final Function<? super B, ? extends T> inverseMapper
135     ) {
136         requireNonNull(mapper);
137         requireNonNull(inverseMapper);
138 
139         return InvertibleCodec.of(
140             encoding(),
141             mapper.compose(decoder()),
142             encoder().compose(inverseMapper)
143         );
144     }
145 
146     /**
147      * Create a new invertible codec from the given parameters.
148      *
149      @param encoding the genotype factory used for creating new
150      *        {@code Genotypes}
151      @param decoder decoder function, which converts a {@link Genotype} to a
152      *        value in the problem domain.
153      @param encoder encoder function, which converts a value of the problem
154      *        domain into a {@link Genotype}
155      @param <G> the {@link Gene} type
156      @param <T> the fitness function argument type in the problem domain
157      @return a new {@link InvertibleCodec} object with the given parameters.
158      @throws NullPointerException if one of the arguments is {@code null}.
159      */
160     static <T, G extends Gene<?, G>> InvertibleCodec<T, G> of(
161         final Factory<Genotype<G>> encoding,
162         final Function<? super Genotype<G>, ? extends T> decoder,
163         final Function<? super T, Genotype<G>> encoder
164     ) {
165         requireNonNull(encoding);
166         requireNonNull(decoder);
167         requireNonNull(encoder);
168 
169         return new InvertibleCodec<>() {
170             @Override
171             public Factory<Genotype<G>> encoding() {
172                 return encoding;
173             }
174 
175             @Override
176             @SuppressWarnings("unchecked")
177             public Function<Genotype<G>, T> decoder() {
178                 return (Function<Genotype<G>, T>)decoder;
179             }
180 
181             @Override
182             @SuppressWarnings("unchecked")
183             public Function<T, Genotype<G>> encoder() {
184                 return (Function<T, Genotype<G>>)encoder;
185             }
186         };
187     }
188 
189 }