001/* 002 * Java Genetic Algorithm Library (jenetics-9.0.0). 003 * Copyright (c) 2007-2026 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(new DoubleRange(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 = new DoubleRange(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(new DoubleRange(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(new DoubleRange(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 * Converts two given {@code Codec} instances into one. This lets you divide 233 * a problem into subproblems and combine them again. 234 * <p> 235 * The following example shows how to combine two codecs, which converts a 236 * {@code LongGene} to a {@code LocalDate}, to a codec which combines the 237 * two {@code LocalDate} object (these are the argument types of the 238 * component codecs) to a {@code Duration}. 239 * {@snippet lang = "java": 240 * final Codec<LocalDate, LongGene> dateCodec1 = Codec.of( 241 * Genotype.of(LongChromosome.of(0, 10_000)), 242 * gt -> LocalDate.ofEpochDay(gt.gene().longValue()) 243 * ); 244 * 245 * final Codec<LocalDate, LongGene> dateCodec2 = Codec.of( 246 * Genotype.of(LongChromosome.of(1_000_000, 10_000_000)), 247 * gt -> LocalDate.ofEpochDay(gt.gene().longValue()) 248 * ); 249 * 250 * final Codec<Duration, LongGene> durationCodec = Codec.combine( 251 * dateCodec1, 252 * dateCodec2, 253 * (d1, d2) -> Duration.ofDays(d2.toEpochDay() - d1.toEpochDay()) 254 * ); 255 * 256 * final Engine<LongGene, Long> engine = Engine 257 * .builder(Duration::toMillis, durationCodec) 258 * .build(); 259 * 260 * final Phenotype<LongGene, Long> pt = engine.stream() 261 * .limit(100) 262 * .collect(EvolutionResult.toBestPhenotype()); 263 * System.out.println(pt); 264 * 265 * final Duration duration = durationCodec.decoder() 266 * .apply(pt.genotype()); 267 * System.out.println(duration); 268 *} 269 * 270 * @since 8.1 271 * 272 * @param <G> the gene type 273 * @param <A> the argument type of the first codec 274 * @param <B> the argument type of the second codec 275 * @param <T> the argument type of the compound codec 276 * @param codec1 the first codec 277 * @param codec2 the second codec 278 * @param decoder the decoder which combines the two argument types from the 279 * given codecs, to the argument type of the resulting codec. 280 * @return a new codec which combines the given {@code codec1} and 281 * {@code codec2} 282 * @throws NullPointerException if one of the arguments is {@code null} 283 */ 284 static <A, B, T, G extends Gene<?, G>> Codec<T, G> combine( 285 final Codec<? extends A, G> codec1, 286 final Codec<? extends B, G> codec2, 287 final BiFunction<? super A, ? super B, ? extends T> decoder 288 ) { 289 @SuppressWarnings("unchecked") 290 final Function<Object[], T> decoderAdapter = 291 v -> decoder.apply((A)v[0], (B)v[1]); 292 293 return combine( 294 ISeq.of(codec1, codec2), 295 decoderAdapter 296 ); 297 } 298 299 /** 300 * Combines the given {@code codecs} into one codec. This lets you divide 301 * a problem into subproblems and combine them again. 302 * <p> 303 * The following example combines more than two sub-codecs into one. 304 * {@snippet lang="java": 305 * final Codec<LocalDate, LongGene> dateCodec = Codec.of( 306 * Genotype.of(LongChromosome.of(0, 10_000)), 307 * gt -> LocalDate.ofEpochDay(gt.getGene().longValue()) 308 * ); 309 * 310 * final Codec<Duration, LongGene> durationCodec = Codec.combine( 311 * ISeq.of(dateCodec, dateCodec, dateCodec), 312 * dates -> { 313 * final LocalDate ld1 = (LocalDate)dates[0]; 314 * final LocalDate ld2 = (LocalDate)dates[1]; 315 * final LocalDate ld3 = (LocalDate)dates[2]; 316 * 317 * return Duration.ofDays( 318 * ld1.toEpochDay() + ld2.toEpochDay() - ld3.toEpochDay() 319 * ); 320 * } 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 <T> the argument type of the compound codec 341 * @param codecs the {@code Codec} sequence of the subproblems 342 * @param decoder the decoder which combines the argument types from the 343 * given codecs, to the argument type of the resulting codec. 344 * @return a new codec which combines the given {@code codecs} 345 * @throws NullPointerException if one of the arguments is {@code null} 346 * @throws IllegalArgumentException if the given {@code codecs} sequence is 347 * empty 348 */ 349 static <T, G extends Gene<?, G>> Codec<T, G> combine( 350 final ISeq<? extends Codec<?, G>> codecs, 351 final Function<? super Object[], ? extends T> decoder 352 ) { 353 if (codecs.isEmpty()) { 354 throw new IllegalArgumentException( 355 "Codecs sequence must not be empty." 356 ); 357 } 358 return codecs.size() == 1 359 ? Codec.of( 360 codecs.get(0).encoding(), 361 gt -> { 362 final Object value = codecs.get(0).decoder().apply(gt); 363 return decoder.apply(new Object[]{value}); 364 }) 365 : new CompositeCodec<>(codecs, decoder); 366 } 367 368}