/* * Java Genetic Algorithm Library (@__identifier__@). * Copyright (c) @__year__@ Franz Wilhelmstötter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) */ package org.jenetics.engine; import static java.util.Objects.requireNonNull; import java.util.function.BiFunction; import java.util.function.Function; import org.jenetics.Gene; import org.jenetics.Genotype; import org.jenetics.util.Factory; import org.jenetics.util.ISeq; /** * A problem {@code Codec} contains the information about how to encode a given * argument type into a {@code Genotype}. It also lets convert the encoded * {@code Genotype} back to the argument type. The engine creation and the * implementation of the fitness function can be heavily simplified by using * a {@code Codec} class. The example given in the {@link Engine} documentation * can be simplified as follows: * * <pre>{@code * public class RealFunction { * // The conversion from the 'Genotype' to the argument type of the fitness * // function is performed by the given 'Codec'. You can concentrate on the * // implementation, because you are not bothered with the conversion code. * private static double eval(final double x) { * return cos(0.5 + sin(x)) * cos(x); * } * * public static void main(final String[] args) { * final Engine<DoubleGene, Double> engine = Engine * // Create an Engine.Builder with the "pure" fitness function * // and the appropriate Codec. * .build(RealFunction::eval, codecs.ofScalar(DoubleRange.of(0, 2*PI))) * .build(); * ... * } * } * }</pre> * * The {@code Codec} needed for the above usage example, will look like this: * <pre>{@code * final DoubleRange domain = DoubleRange.of(0, 2*PI); * final Codec<Double, DoubleGene> codec = Codec.of( * Genotype.of(DoubleChromosome.of(domain)), * gt -> gt.getChromosome().getGene().getAllele() * ); * }</pre> * * Calling the {@link Codec#of(Factory, Function)} method is the usual way for * creating new {@code Codec} instances. * * @see codecs * @see Engine * @see Engine.Builder * * @param <T> the argument type of a given problem * @param <G> the {@code Gene} type used for encoding the argument type {@code T} * * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a> * @version 3.6 * @since 3.2 */ public interface Codec<T, G extends Gene<?, G>> { /** * Return the genotype factory for creating genotypes with the right * encoding for the given problem. The genotype created with this factory * must work together with the {@link #decoder()} function, which transforms * the genotype into an object of the problem domain. * * <pre>{@code * final Codec<SomeObject, DoubleGene> codec = ... * final Genotype<DoubleGene> gt = codec.encoding().newInstance(); * final SomeObject arg = codec.decoder().apply(gt); * }</pre> * * @see #decoder() * * @return the genotype (factory) representation of the problem domain */ public Factory<Genotype<G>> encoding(); /** * Return the <em>decoder</em> function which transforms the genotype back * to the original problem domain representation. * * @see #encoding() * * @return genotype decoder */ public Function<Genotype<G>, T> decoder(); /** * Converts the given {@link Genotype} to the target type {@link T}. This is * a shortcut for * <pre>{@code * final Codec<SomeObject, DoubleGene> codec = ... * final Genotype<DoubleGene> gt = codec.encoding().newInstance(); * * final SomeObject arg = codec.decoder().apply(gt); * }</pre> * * @since 3.6 * * @param gt the genotype to be converted * @return the converted genotype */ public default T decode(final Genotype<G> gt) { return decoder().apply(gt); } /** * Create a new {@code Codec} object with the given {@code encoding} and * {@code decoder} function. * * @param encoding the genotype factory used for creating new * {@code Genotypes}. * @param decoder decoder function, which converts a {@code Genotype} to a * value in the problem domain. * @param <G> the {@code Gene} type * @param <T> the fitness function argument type in the problem domain * @return a new {@code Codec} object with the given parameters. * @throws NullPointerException if one of the arguments is {@code null}. */ public static <G extends Gene<?, G>, T> Codec<T, G> of( final Factory<Genotype<G>> encoding, final Function<Genotype<G>, T> decoder ) { requireNonNull(encoding); requireNonNull(decoder); return new Codec<T, G>() { @Override public Factory<Genotype<G>> encoding() { return encoding; } @Override public Function<Genotype<G>, T> decoder() { return decoder; } }; } /** * Converts two given {@code Codec} instances into one. This lets you divide * a problem into sub problems and combine them again. * <p> * The following example shows how to combine two codecs, which converts a * {@code LongGene} to a {@code LocalDate}, to a codec which combines the * two {@code LocalDate} object (this are the argument types of the * component codecs) to a {@code Duration}. * * <pre>{@code * final Codec<LocalDate, LongGene> dateCodec1 = Codec.of( * Genotype.of(LongChromosome.of(0, 10_000)), * gt -> LocalDate.ofEpochDay(gt.getGene().longValue()) * ); * * final Codec<LocalDate, LongGene> dateCodec2 = Codec.of( * Genotype.of(LongChromosome.of(1_000_000, 10_000_000)), * gt -> LocalDate.ofEpochDay(gt.getGene().longValue()) * ); * * final Codec<Duration, LongGene> durationCodec = Codec.of( * dateCodec1, * dateCodec2, * (d1, d2) -> Duration.ofDays(d2.toEpochDay() - d1.toEpochDay()) * ); * * final Engine<LongGene, Long> engine = Engine * .builder(Duration::toMillis, durationCodec) * .build(); * * final Phenotype<LongGene, Long> pt = engine.stream() * .limit(100) * .collect(EvolutionResult.toBestPhenotype()); * System.out.println(pt); * * final Duration duration = durationCodec.decoder() * .apply(pt.getGenotype()); * System.out.println(duration); * }</pre> * * @since 3.3 * * @param <G> the gene type * @param <A> the argument type of the first codec * @param <B> the argument type of the second codec * @param <T> the argument type of the compound codec * @param codec1 the first codec * @param codec2 the second codec * @param decoder the decoder which combines the two argument types from the * given given codecs, to the argument type of the resulting codec. * @return a new codec which combines the given {@code codec1} and * {@code codec2} * @throws NullPointerException if one of the arguments is {@code null} */ public static <G extends Gene<?, G>, A, B, T> Codec<T, G> of( final Codec<A, G> codec1, final Codec<B, G> codec2, final BiFunction<A, B, T> decoder ) { @SuppressWarnings("unchecked") final Function<Object[], T> decoderAdapter = v -> decoder.apply((A)v[0], (B)v[1]); return of( ISeq.of(codec1, codec2), decoderAdapter ); } /** * Combines the given {@code codecs} into one codec. This lets you divide * a problem into sub problems and combine them again. * <p> * The following example combines more than two sub-codecs into one. * <pre>{@code * final Codec<LocalDate, LongGene> dateCodec = Codec.of( * Genotype.of(LongChromosome.of(0, 10_000)), * gt -> LocalDate.ofEpochDay(gt.getGene().longValue()) * ); * * final Codec<Duration, LongGene> durationCodec = Codec.of( * ISeq.of(dateCodec, dateCodec, dateCodec), * dates -> { * final LocalDate ld1 = (LocalDate)dates[0]; * final LocalDate ld2 = (LocalDate)dates[1]; * final LocalDate ld3 = (LocalDate)dates[2]; * * return Duration.ofDays( * ld1.toEpochDay() + ld2.toEpochDay() - ld3.toEpochDay() * ); * } * ); * * final Engine<LongGene, Long> engine = Engine * .builder(Duration::toMillis, durationCodec) * .build(); * * final Phenotype<LongGene, Long> pt = engine.stream() * .limit(100) * .collect(EvolutionResult.toBestPhenotype()); * System.out.println(pt); * * final Duration duration = durationCodec.decoder() * .apply(pt.getGenotype()); * System.out.println(duration); * }</pre> * * @since 3.3 * * @param <G> the gene type * @param <T> the argument type of the compound codec * @param codecs the {@code Codec} sequence of the sub-problems * @param decoder the decoder which combines the argument types from the * given given codecs, to the argument type of the resulting codec. * @return a new codec which combines the given {@code codecs} * @throws NullPointerException if one of the arguments is {@code null} */ public static <G extends Gene<?, G>, T> Codec<T, G> of( final ISeq<? extends Codec<?, G>> codecs, final Function<? super Object[], ? extends T> decoder ) { return new CompositeCodec<>(codecs, decoder); } }