001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 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.BiFunction; 025import java.util.function.Function; 026 027import io.jenetics.Gene; 028import io.jenetics.Genotype; 029import io.jenetics.util.Factory; 030import 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 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 */ 083public 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 whose 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 (these 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 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 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}