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 */
020package io.jenetics.engine;
021
022import static java.util.Objects.requireNonNull;
023
024import java.util.function.Function;
025import java.util.function.Predicate;
026
027import io.jenetics.Gene;
028import io.jenetics.Genotype;
029import 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 */
061public 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}