001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.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.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 * {@snippet lang="java": 040 * public class RealFunction { 041 * // The conversion from the 'Genotype' to the argument type of the fitness 042 * // function is performed by the given 'Codec'. You can concentrate on the 043 * // implementation, because you are not bothered with the conversion code. 044 * private static double eval(final double x) { 045 * return cos(0.5 + sin(x)) * cos(x); 046 * } 047 * 048 * public static void main(final String[] args) { 049 * final Engine<DoubleGene, Double> engine = Engine 050 * // Create an Engine.Builder with the "pure" fitness function 051 * // and the appropriate Codec. 052 * .build(RealFunction::eval, Codecs.ofScalar(DoubleRange.of(0, 2*PI))) 053 * .build(); 054 * // ... 055 * } 056 * } 057 * } 058 * 059 * The {@code Codec} needed for the above usage example, will look like this: 060 * {@snippet lang="java": 061 * final DoubleRange domain = DoubleRange.of(0, 2*PI); 062 * final Codec<Double, DoubleGene> codec = Codec.of( 063 * Genotype.of(DoubleChromosome.of(domain)), 064 * gt -> gt.chromosome().gene().allele() 065 * ); 066 * } 067 * 068 * Calling the {@link Codec#of(Factory, Function)} method is the usual way for 069 * creating new {@code Codec} instances. 070 * 071 * @see Codecs 072 * @see Engine 073 * @see Engine.Builder 074 * 075 * @param <T> the argument type of given problem 076 * @param <G> the {@code Gene} type used for encoding the argument type {@code T} 077 * 078 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 079 * @version 3.6 080 * @since 3.2 081 */ 082public interface Codec<T, G extends Gene<?, G>> { 083 084 /** 085 * Return the genotype factory for creating genotypes with the right 086 * encoding for the given problem. The genotype created with this factory 087 * must work together with the {@link #decoder()} function, which transforms 088 * the genotype into an object of the problem domain. 089 * {@snippet lang="java": 090 * final Codec<SomeObject, DoubleGene> codec = null; // @replace substring='null' replacement="..." 091 * final Genotype<DoubleGene> gt = codec.encoding().newInstance(); 092 * final SomeObject arg = codec.decoder().apply(gt); 093 * } 094 * 095 * @see #decoder() 096 * 097 * @return the genotype (factory) representation of the problem domain 098 */ 099 Factory<Genotype<G>> encoding(); 100 101 /** 102 * Return the <em>decoder</em> function which transforms the genotype back 103 * to the original problem domain representation. 104 * 105 * @see #encoding() 106 * 107 * @return genotype decoder 108 */ 109 Function<Genotype<G>, T> decoder(); 110 111 /** 112 * Converts the given {@link Genotype} to the target type {@link T}. This is 113 * a shortcut for 114 * {@snippet lang="java": 115 * final Codec<SomeObject, DoubleGene> codec = null; // @replace substring='null' replacement="..." 116 * final Genotype<DoubleGene> gt = codec.encoding().newInstance(); 117 * 118 * final SomeObject arg = codec.decoder().apply(gt); 119 * } 120 * 121 * @since 3.6 122 * 123 * @param genotype the genotype to be converted 124 * @return the converted genotype 125 * @throws NullPointerException if the given {@code genotype} is {@code null} 126 */ 127 default T decode(final Genotype<G> genotype) { 128 requireNonNull(genotype); 129 return decoder().apply(genotype); 130 } 131 132 /** 133 * Create a new {@code Codec} with the mapped result type. The following 134 * example creates a double codec whose values are not uniformly distributed 135 * between {@code [0..1)}. Instead, the values now follow an exponential 136 * function. 137 * {@snippet lang="java": 138 * final Codec<Double, DoubleGene> c = Codecs.ofScalar(DoubleRange.of(0, 1)) 139 * .map(Math::exp); 140 * } 141 * 142 * This method can also be used for creating non-trivial codes like split 143 * ranges, as shown in the following example, where only values between 144 * <em>[0, 2)</em> and <em>[8, 10)</em> are valid. 145 * <pre>{@code 146 * +--+--+--+--+--+--+--+--+--+--+ 147 * | | | | | | | | | | | 148 * 0 1 2 3 4 5 6 7 8 9 10 149 * |-----|xxxxxxxxxxxxxxxxx|-----| 150 * ^ |llllllll|rrrrrrrr| ^ 151 * | | | | 152 * +-------+ +------+ 153 * }</pre> 154 * 155 * {@snippet lang="java": 156 * final Codec<Double, DoubleGene> codec = Codecs 157 * .ofScalar(DoubleRange.of(0, 10)) 158 * .map(v -> { 159 * if (v >= 2 && v < 8) { 160 * return v < 5 ? ((v - 2)/3)*2 : ((8 - v)/3)*2 + 8; 161 * } 162 * return v; 163 * }); 164 * } 165 * 166 * @since 4.0 167 * 168 * @see InvertibleCodec#map(Function, Function) 169 * 170 * @param mapper the mapper function 171 * @param <B> the new argument type of the given problem 172 * @return a new {@code Codec} with the mapped result type 173 * @throws NullPointerException if the mapper is {@code null}. 174 */ 175 default <B> Codec<B, G> map(final Function<? super T, ? extends B> mapper) { 176 requireNonNull(mapper); 177 178 return Codec.of( 179 encoding(), 180 mapper.compose(decoder()) 181 ); 182 } 183 184 /** 185 * Converts this codec into an <em>invertible</em> codec, by using the given 186 * {@code encoder} (inversion) function. 187 * 188 * @param encoder the (inverse) encoder function 189 * @return a new invertible codec 190 * @throws NullPointerException if the given {@code encoder} is {@code null} 191 */ 192 default InvertibleCodec<T, G> 193 toInvertibleCodec(final Function<? super T, Genotype<G>> encoder) { 194 return InvertibleCodec.of(encoding(), decoder(), encoder); 195 } 196 197 /** 198 * Create a new {@code Codec} object with the given {@code encoding} and 199 * {@code decoder} function. 200 * 201 * @param encoding the genotype factory used for creating new 202 * {@code Genotypes} 203 * @param decoder decoder function, which converts a {@code Genotype} to a 204 * value in the problem domain 205 * @param <G> the {@code Gene} type 206 * @param <T> the fitness function argument type in the problem domain 207 * @return a new {@code Codec} object with the given parameters 208 * @throws NullPointerException if one of the arguments is {@code null}. 209 */ 210 static <T, G extends Gene<?, G>> Codec<T, G> of( 211 final Factory<Genotype<G>> encoding, 212 final Function<? super Genotype<G>, ? extends T> decoder 213 ) { 214 requireNonNull(encoding); 215 requireNonNull(decoder); 216 217 return new Codec<>() { 218 @Override 219 public Factory<Genotype<G>> encoding() { 220 return encoding; 221 } 222 223 @Override 224 @SuppressWarnings("unchecked") 225 public Function<Genotype<G>, T> decoder() { 226 return (Function<Genotype<G>, T>)decoder; 227 } 228 }; 229 } 230 231 232 /** 233 * Converts two given {@code Codec} instances into one. This lets you divide 234 * a problem into subproblems and combine them again. 235 * <p> 236 * The following example shows how to combine two codecs, which converts a 237 * {@code LongGene} to a {@code LocalDate}, to a codec which combines the 238 * two {@code LocalDate} object (these are the argument types of the 239 * component codecs) to a {@code Duration}. 240 * {@snippet lang = "java": 241 * final Codec<LocalDate, LongGene> dateCodec1 = Codec.of( 242 * Genotype.of(LongChromosome.of(0, 10_000)), 243 * gt -> LocalDate.ofEpochDay(gt.gene().longValue()) 244 * ); 245 * 246 * final Codec<LocalDate, LongGene> dateCodec2 = Codec.of( 247 * Genotype.of(LongChromosome.of(1_000_000, 10_000_000)), 248 * gt -> LocalDate.ofEpochDay(gt.gene().longValue()) 249 * ); 250 * 251 * final Codec<Duration, LongGene> durationCodec = Codec.of( 252 * dateCodec1, 253 * dateCodec2, 254 * (d1, d2) -> Duration.ofDays(d2.toEpochDay() - d1.toEpochDay()) 255 * ); 256 * 257 * final Engine<LongGene, Long> engine = Engine 258 * .builder(Duration::toMillis, durationCodec) 259 * .build(); 260 * 261 * final Phenotype<LongGene, Long> pt = engine.stream() 262 * .limit(100) 263 * .collect(EvolutionResult.toBestPhenotype()); 264 * System.out.println(pt); 265 * 266 * final Duration duration = durationCodec.decoder() 267 * .apply(pt.genotype()); 268 * System.out.println(duration); 269 *} 270 * 271 * @since 3.3 272 * 273 * @see #combine(Codec, Codec, BiFunction) 274 * 275 * @param <G> the gene type 276 * @param <A> the argument type of the first codec 277 * @param <B> the argument type of the second codec 278 * @param <T> the argument type of the compound codec 279 * @param codec1 the first codec 280 * @param codec2 the second codec 281 * @param decoder the decoder which combines the two argument types from the 282 * given codecs, to the argument type of the resulting codec. 283 * @return a new codec which combines the given {@code codec1} and 284 * {@code codec2} 285 * @throws NullPointerException if one of the arguments is {@code null} 286 * @deprecated Will be removed in the next major version, use 287 * {@link #combine(Codec, Codec, BiFunction)} instead 288 */ 289 @Deprecated(since = "8.1", forRemoval = true) 290 static <A, B, T, G extends Gene<?, G>> Codec<T, G> of( 291 final Codec<A, G> codec1, 292 final Codec<B, G> codec2, 293 final BiFunction<A, B, T> decoder 294 ) { 295 return combine(codec1, codec2, decoder); 296 } 297 298 /** 299 * Converts two given {@code Codec} instances into one. This lets you divide 300 * a problem into subproblems and combine them again. 301 * <p> 302 * The following example shows how to combine two codecs, which converts a 303 * {@code LongGene} to a {@code LocalDate}, to a codec which combines the 304 * two {@code LocalDate} object (these are the argument types of the 305 * component codecs) to a {@code Duration}. 306 * {@snippet lang = "java": 307 * final Codec<LocalDate, LongGene> dateCodec1 = Codec.of( 308 * Genotype.of(LongChromosome.of(0, 10_000)), 309 * gt -> LocalDate.ofEpochDay(gt.gene().longValue()) 310 * ); 311 * 312 * final Codec<LocalDate, LongGene> dateCodec2 = Codec.of( 313 * Genotype.of(LongChromosome.of(1_000_000, 10_000_000)), 314 * gt -> LocalDate.ofEpochDay(gt.gene().longValue()) 315 * ); 316 * 317 * final Codec<Duration, LongGene> durationCodec = Codec.combine( 318 * dateCodec1, 319 * dateCodec2, 320 * (d1, d2) -> Duration.ofDays(d2.toEpochDay() - d1.toEpochDay()) 321 * ); 322 * 323 * final Engine<LongGene, Long> engine = Engine 324 * .builder(Duration::toMillis, durationCodec) 325 * .build(); 326 * 327 * final Phenotype<LongGene, Long> pt = engine.stream() 328 * .limit(100) 329 * .collect(EvolutionResult.toBestPhenotype()); 330 * System.out.println(pt); 331 * 332 * final Duration duration = durationCodec.decoder() 333 * .apply(pt.genotype()); 334 * System.out.println(duration); 335 *} 336 * 337 * @since 8.1 338 * 339 * @param <G> the gene type 340 * @param <A> the argument type of the first codec 341 * @param <B> the argument type of the second codec 342 * @param <T> the argument type of the compound codec 343 * @param codec1 the first codec 344 * @param codec2 the second codec 345 * @param decoder the decoder which combines the two argument types from the 346 * given codecs, to the argument type of the resulting codec. 347 * @return a new codec which combines the given {@code codec1} and 348 * {@code codec2} 349 * @throws NullPointerException if one of the arguments is {@code null} 350 */ 351 static <A, B, T, G extends Gene<?, G>> Codec<T, G> combine( 352 final Codec<? extends A, G> codec1, 353 final Codec<? extends B, G> codec2, 354 final BiFunction<? super A, ? super B, ? extends T> decoder 355 ) { 356 @SuppressWarnings("unchecked") 357 final Function<Object[], T> decoderAdapter = 358 v -> decoder.apply((A)v[0], (B)v[1]); 359 360 return combine( 361 ISeq.of(codec1, codec2), 362 decoderAdapter 363 ); 364 } 365 366 /** 367 * Combines the given {@code codecs} into one codec. This lets you divide 368 * a problem into subproblems and combine them again. 369 * <p> 370 * The following example combines more than two sub-codecs into one. 371 * {@snippet lang="java": 372 * final Codec<LocalDate, LongGene> dateCodec = Codec.of( 373 * Genotype.of(LongChromosome.of(0, 10_000)), 374 * gt -> LocalDate.ofEpochDay(gt.getGene().longValue()) 375 * ); 376 * 377 * final Codec<Duration, LongGene> durationCodec = Codec.of( 378 * ISeq.of(dateCodec, dateCodec, dateCodec), 379 * dates -> { 380 * final LocalDate ld1 = (LocalDate)dates[0]; 381 * final LocalDate ld2 = (LocalDate)dates[1]; 382 * final LocalDate ld3 = (LocalDate)dates[2]; 383 * 384 * return Duration.ofDays( 385 * ld1.toEpochDay() + ld2.toEpochDay() - ld3.toEpochDay() 386 * ); 387 * } 388 * ); 389 * 390 * final Engine<LongGene, Long> engine = Engine 391 * .builder(Duration::toMillis, durationCodec) 392 * .build(); 393 * 394 * final Phenotype<LongGene, Long> pt = engine.stream() 395 * .limit(100) 396 * .collect(EvolutionResult.toBestPhenotype()); 397 * System.out.println(pt); 398 * 399 * final Duration duration = durationCodec.decoder() 400 * .apply(pt.genotype()); 401 * System.out.println(duration); 402 * } 403 * 404 * @since 3.3 405 * 406 * @param <G> the gene type 407 * @param <T> the argument type of the compound codec 408 * @param codecs the {@code Codec} sequence of the subproblems 409 * @param decoder the decoder which combines the argument types from the 410 * given codecs, to the argument type of the resulting codec. 411 * @return a new codec which combines the given {@code codecs} 412 * @throws NullPointerException if one of the arguments is {@code null} 413 * @throws IllegalArgumentException if the given {@code codecs} sequence is 414 * empty 415 * @deprecated Will be removed in the next major version, use 416 * {@link #combine(ISeq, Function)} instead 417 */ 418 @Deprecated(since = "8.1", forRemoval = true) 419 static <T, G extends Gene<?, G>> Codec<T, G> of( 420 final ISeq<? extends Codec<?, G>> codecs, 421 final Function<? super Object[], ? extends T> decoder 422 ) { 423 return combine(codecs, decoder); 424 } 425 426 /** 427 * Combines the given {@code codecs} into one codec. This lets you divide 428 * a problem into subproblems and combine them again. 429 * <p> 430 * The following example combines more than two sub-codecs into one. 431 * {@snippet lang="java": 432 * final Codec<LocalDate, LongGene> dateCodec = Codec.of( 433 * Genotype.of(LongChromosome.of(0, 10_000)), 434 * gt -> LocalDate.ofEpochDay(gt.getGene().longValue()) 435 * ); 436 * 437 * final Codec<Duration, LongGene> durationCodec = Codec.combine( 438 * ISeq.of(dateCodec, dateCodec, dateCodec), 439 * dates -> { 440 * final LocalDate ld1 = (LocalDate)dates[0]; 441 * final LocalDate ld2 = (LocalDate)dates[1]; 442 * final LocalDate ld3 = (LocalDate)dates[2]; 443 * 444 * return Duration.ofDays( 445 * ld1.toEpochDay() + ld2.toEpochDay() - ld3.toEpochDay() 446 * ); 447 * } 448 * ); 449 * 450 * final Engine<LongGene, Long> engine = Engine 451 * .builder(Duration::toMillis, durationCodec) 452 * .build(); 453 * 454 * final Phenotype<LongGene, Long> pt = engine.stream() 455 * .limit(100) 456 * .collect(EvolutionResult.toBestPhenotype()); 457 * System.out.println(pt); 458 * 459 * final Duration duration = durationCodec.decoder() 460 * .apply(pt.genotype()); 461 * System.out.println(duration); 462 * } 463 * 464 * @since 8.1 465 * 466 * @param <G> the gene type 467 * @param <T> the argument type of the compound codec 468 * @param codecs the {@code Codec} sequence of the subproblems 469 * @param decoder the decoder which combines the argument types from the 470 * given codecs, to the argument type of the resulting codec. 471 * @return a new codec which combines the given {@code codecs} 472 * @throws NullPointerException if one of the arguments is {@code null} 473 * @throws IllegalArgumentException if the given {@code codecs} sequence is 474 * empty 475 */ 476 static <T, G extends Gene<?, G>> Codec<T, G> combine( 477 final ISeq<? extends Codec<?, G>> codecs, 478 final Function<? super Object[], ? extends T> decoder 479 ) { 480 if (codecs.isEmpty()) { 481 throw new IllegalArgumentException( 482 "Codecs sequence must not be empty." 483 ); 484 } 485 return codecs.size() == 1 486 ? Codec.of( 487 codecs.get(0).encoding(), 488 gt -> { 489 final Object value = codecs.get(0).decoder().apply(gt); 490 return decoder.apply(new Object[]{value}); 491 }) 492 : new CompositeCodec<>(codecs, decoder); 493 } 494 495}