001 /*
002 * Java Genetic Algorithm Library (jenetics-6.3.0).
003 * Copyright (c) 2007-2021 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.BiFunction;
025 import java.util.function.Function;
026
027 import io.jenetics.Gene;
028 import io.jenetics.Genotype;
029 import io.jenetics.util.Factory;
030 import io.jenetics.util.ISeq;
031
032 /**
033 * A problem {@code Codec} contains the information about how to encode a given
034 * argument type into a {@code Genotype}. It also lets convert the encoded
035 * {@code Genotype} back to the argument type. The engine creation and the
036 * implementation of the fitness function can be heavily simplified by using
037 * a {@code Codec} class. The example given in the {@link Engine} documentation
038 * can be simplified as follows:
039 *
040 * <pre>{@code
041 * public class RealFunction {
042 * // The conversion from the 'Genotype' to the argument type of the fitness
043 * // function is performed by the given 'Codec'. You can concentrate on the
044 * // implementation, because you are not bothered with the conversion code.
045 * private static double eval(final double x) {
046 * return cos(0.5 + sin(x)) * cos(x);
047 * }
048 *
049 * public static void main(final String[] args) {
050 * final Engine<DoubleGene, Double> engine = Engine
051 * // Create an Engine.Builder with the "pure" fitness function
052 * // and the appropriate Codec.
053 * .build(RealFunction::eval, Codecs.ofScalar(DoubleRange.of(0, 2*PI)))
054 * .build();
055 * ...
056 * }
057 * }
058 * }</pre>
059 *
060 * The {@code Codec} needed for the above usage example, will look like this:
061 * <pre>{@code
062 * final DoubleRange domain = DoubleRange.of(0, 2*PI);
063 * final Codec<Double, DoubleGene> codec = Codec.of(
064 * Genotype.of(DoubleChromosome.of(domain)),
065 * gt -> gt.chromosome().gene().allele()
066 * );
067 * }</pre>
068 *
069 * Calling the {@link Codec#of(Factory, Function)} method is the usual way for
070 * creating new {@code Codec} instances.
071 *
072 * @see Codecs
073 * @see Engine
074 * @see Engine.Builder
075 *
076 * @param <T> the argument type of a given problem
077 * @param <G> the {@code Gene} type used for encoding the argument type {@code T}
078 *
079 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
080 * @version 3.6
081 * @since 3.2
082 */
083 public interface Codec<T, G extends Gene<?, G>> {
084
085 /**
086 * Return the genotype factory for creating genotypes with the right
087 * encoding for the given problem. The genotype created with this factory
088 * must work together with the {@link #decoder()} function, which transforms
089 * the genotype into an object of the problem domain.
090 *
091 * <pre>{@code
092 * final Codec<SomeObject, DoubleGene> codec = ...
093 * final Genotype<DoubleGene> gt = codec.encoding().newInstance();
094 * final SomeObject arg = codec.decoder().apply(gt);
095 * }</pre>
096 *
097 * @see #decoder()
098 *
099 * @return the genotype (factory) representation of the problem domain
100 */
101 Factory<Genotype<G>> encoding();
102
103 /**
104 * Return the <em>decoder</em> function which transforms the genotype back
105 * to the original problem domain representation.
106 *
107 * @see #encoding()
108 *
109 * @return genotype decoder
110 */
111 Function<Genotype<G>, T> decoder();
112
113 /**
114 * Converts the given {@link Genotype} to the target type {@link T}. This is
115 * a shortcut for
116 * <pre>{@code
117 * final Codec<SomeObject, DoubleGene> codec = ...
118 * final Genotype<DoubleGene> gt = codec.encoding().newInstance();
119 *
120 * final SomeObject arg = codec.decoder().apply(gt);
121 * }</pre>
122 *
123 * @since 3.6
124 *
125 * @param genotype the genotype to be converted
126 * @return the converted genotype
127 * @throws NullPointerException if the given {@code genotype} is {@code null}
128 */
129 default T decode(final Genotype<G> genotype) {
130 requireNonNull(genotype);
131 return decoder().apply(genotype);
132 }
133
134 /**
135 * Create a new {@code Codec} with the mapped result type. The following
136 * example creates a double codec who's values are not uniformly distributed
137 * between {@code [0..1)}. Instead the values now follow an exponential
138 * function.
139 *
140 * <pre>{@code
141 * final Codec<Double, DoubleGene> c = Codecs.ofScalar(DoubleRange.of(0, 1))
142 * .map(Math::exp);
143 * }</pre>
144 *
145 * This method can also be used for creating non-trivial codes like split
146 * ranges, as shown in the following example, where only values between
147 * <em>[0, 2)</em> and <em>[8, 10)</em> are valid.
148 * <pre>{@code
149 * +--+--+--+--+--+--+--+--+--+--+
150 * | | | | | | | | | | |
151 * 0 1 2 3 4 5 6 7 8 9 10
152 * |-----|xxxxxxxxxxxxxxxxx|-----|
153 * ^ |llllllll|rrrrrrrr| ^
154 * | | | |
155 * +-------+ +------+
156 * }</pre>
157 *
158 * <pre>{@code
159 * final Codec<Double, DoubleGene> codec = Codecs
160 * .ofScalar(DoubleRange.of(0, 10))
161 * .map(v -> {
162 * if (v >= 2 && v < 8) {
163 * return v < 5 ? ((v - 2)/3)*2 : ((8 - v)/3)*2 + 8;
164 * }
165 * return v;
166 * });
167 * }</pre>
168 *
169 * @since 4.0
170 *
171 * @see InvertibleCodec#map(Function, Function)
172 *
173 * @param mapper the mapper function
174 * @param <B> the new argument type of the given problem
175 * @return a new {@code Codec} with the mapped result type
176 * @throws NullPointerException if the mapper is {@code null}.
177 */
178 default <B> Codec<B, G> map(final Function<? super T, ? extends B> mapper) {
179 requireNonNull(mapper);
180
181 return Codec.of(
182 encoding(),
183 mapper.compose(decoder())
184 );
185 }
186
187 /**
188 * Converts this codec into an <em>invertible</em> codec, by using the given
189 * {@code encoder} (inversion) function.
190 *
191 * @param encoder the (inverse) encoder function
192 * @return a new invertible codec
193 * @throws NullPointerException if the given {@code encoder} is {@code null}
194 */
195 default InvertibleCodec<T, G>
196 toInvertibleCodec(final Function<? super T, Genotype<G>> encoder) {
197 return InvertibleCodec.of(
198 encoding(),
199 decoder(),
200 encoder
201 );
202 }
203
204 /**
205 * Create a new {@code Codec} object with the given {@code encoding} and
206 * {@code decoder} function.
207 *
208 * @param encoding the genotype factory used for creating new
209 * {@code Genotypes}
210 * @param decoder decoder function, which converts a {@code Genotype} to a
211 * value in the problem domain
212 * @param <G> the {@code Gene} type
213 * @param <T> the fitness function argument type in the problem domain
214 * @return a new {@code Codec} object with the given parameters
215 * @throws NullPointerException if one of the arguments is {@code null}.
216 */
217 static <T, G extends Gene<?, G>> Codec<T, G> of(
218 final Factory<Genotype<G>> encoding,
219 final Function<? super Genotype<G>, ? extends T> decoder
220 ) {
221 requireNonNull(encoding);
222 requireNonNull(decoder);
223
224 return new Codec<>() {
225 @Override
226 public Factory<Genotype<G>> encoding() {
227 return encoding;
228 }
229
230 @Override
231 @SuppressWarnings("unchecked")
232 public Function<Genotype<G>, T> decoder() {
233 return (Function<Genotype<G>, T>)decoder;
234 }
235 };
236 }
237
238
239 /**
240 * Converts two given {@code Codec} instances into one. This lets you divide
241 * a problem into sub problems and combine them again.
242 * <p>
243 * The following example shows how to combine two codecs, which converts a
244 * {@code LongGene} to a {@code LocalDate}, to a codec which combines the
245 * two {@code LocalDate} object (this are the argument types of the
246 * component codecs) to a {@code Duration}.
247 *
248 * <pre>{@code
249 * final Codec<LocalDate, LongGene> dateCodec1 = Codec.of(
250 * Genotype.of(LongChromosome.of(0, 10_000)),
251 * gt -> LocalDate.ofEpochDay(gt.gene().longValue())
252 * );
253 *
254 * final Codec<LocalDate, LongGene> dateCodec2 = Codec.of(
255 * Genotype.of(LongChromosome.of(1_000_000, 10_000_000)),
256 * gt -> LocalDate.ofEpochDay(gt.gene().longValue())
257 * );
258 *
259 * final Codec<Duration, LongGene> durationCodec = Codec.of(
260 * dateCodec1,
261 * dateCodec2,
262 * (d1, d2) -> Duration.ofDays(d2.toEpochDay() - d1.toEpochDay())
263 * );
264 *
265 * final Engine<LongGene, Long> engine = Engine
266 * .builder(Duration::toMillis, durationCodec)
267 * .build();
268 *
269 * final Phenotype<LongGene, Long> pt = engine.stream()
270 * .limit(100)
271 * .collect(EvolutionResult.toBestPhenotype());
272 * System.out.println(pt);
273 *
274 * final Duration duration = durationCodec.decoder()
275 * .apply(pt.genotype());
276 * System.out.println(duration);
277 * }</pre>
278 *
279 * @since 3.3
280 *
281 * @param <G> the gene type
282 * @param <A> the argument type of the first codec
283 * @param <B> the argument type of the second codec
284 * @param <T> the argument type of the compound codec
285 * @param codec1 the first codec
286 * @param codec2 the second codec
287 * @param decoder the decoder which combines the two argument types from the
288 * given given codecs, to the argument type of the resulting codec.
289 * @return a new codec which combines the given {@code codec1} and
290 * {@code codec2}
291 * @throws NullPointerException if one of the arguments is {@code null}
292 */
293 static <A, B, T, G extends Gene<?, G>> Codec<T, G> of(
294 final Codec<A, G> codec1,
295 final Codec<B, G> codec2,
296 final BiFunction<A, B, T> decoder
297 ) {
298 @SuppressWarnings("unchecked")
299 final Function<Object[], T> decoderAdapter =
300 v -> decoder.apply((A)v[0], (B)v[1]);
301
302 return of(
303 ISeq.of(codec1, codec2),
304 decoderAdapter
305 );
306 }
307
308 /**
309 * Combines the given {@code codecs} into one codec. This lets you divide
310 * a problem into sub problems and combine them again.
311 * <p>
312 * The following example combines more than two sub-codecs into one.
313 * <pre>{@code
314 * final Codec<LocalDate, LongGene> dateCodec = Codec.of(
315 * Genotype.of(LongChromosome.of(0, 10_000)),
316 * gt -> LocalDate.ofEpochDay(gt.getGene().longValue())
317 * );
318 *
319 * final Codec<Duration, LongGene> durationCodec = Codec.of(
320 * ISeq.of(dateCodec, dateCodec, dateCodec),
321 * dates -> {
322 * final LocalDate ld1 = (LocalDate)dates[0];
323 * final LocalDate ld2 = (LocalDate)dates[1];
324 * final LocalDate ld3 = (LocalDate)dates[2];
325 *
326 * return Duration.ofDays(
327 * ld1.toEpochDay() + ld2.toEpochDay() - ld3.toEpochDay()
328 * );
329 * }
330 * );
331 *
332 * final Engine<LongGene, Long> engine = Engine
333 * .builder(Duration::toMillis, durationCodec)
334 * .build();
335 *
336 * final Phenotype<LongGene, Long> pt = engine.stream()
337 * .limit(100)
338 * .collect(EvolutionResult.toBestPhenotype());
339 * System.out.println(pt);
340 *
341 * final Duration duration = durationCodec.decoder()
342 * .apply(pt.genotype());
343 * System.out.println(duration);
344 * }</pre>
345 *
346 * @since 3.3
347 *
348 * @param <G> the gene type
349 * @param <T> the argument type of the compound codec
350 * @param codecs the {@code Codec} sequence of the sub-problems
351 * @param decoder the decoder which combines the argument types from the
352 * given given codecs, to the argument type of the resulting codec.
353 * @return a new codec which combines the given {@code codecs}
354 * @throws NullPointerException if one of the arguments is {@code null}
355 * @throws IllegalArgumentException if the given {@code codecs} sequence is
356 * empty
357 */
358 static <T, G extends Gene<?, G>> Codec<T, G> of(
359 final ISeq<? extends Codec<?, G>> codecs,
360 final Function<? super Object[], ? extends T> decoder
361 ) {
362 if (codecs.isEmpty()) {
363 throw new IllegalArgumentException(
364 "Codecs sequence must not be empty."
365 );
366 }
367 return codecs.size() == 1
368 ? Codec.of(
369 codecs.get(0).encoding(),
370 gt -> {
371 final Object value = codecs.get(0).decoder().apply(gt);
372 return decoder.apply(new Object[]{value});
373 })
374 : new CompositeCodec<>(codecs, decoder);
375 }
376
377 }
|