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; 021 022import static java.lang.String.format; 023import static java.util.Objects.requireNonNull; 024import static io.jenetics.internal.util.Hashes.hash; 025import static io.jenetics.internal.util.SerialIO.readLong; 026import static io.jenetics.internal.util.SerialIO.writeLong; 027 028import java.io.IOException; 029import java.io.InvalidObjectException; 030import java.io.ObjectInput; 031import java.io.ObjectInputStream; 032import java.io.ObjectOutput; 033import java.io.Serial; 034import java.io.Serializable; 035import java.util.NoSuchElementException; 036import java.util.Objects; 037import java.util.Optional; 038import java.util.function.Function; 039 040import io.jenetics.util.Verifiable; 041 042/** 043 * The {@code Phenotype} consists of a {@link Genotype}, the current generation 044 * and an optional fitness value. Once the fitness has been evaluated, a new 045 * {@code Phenotype} instance, with the calculated fitness, can be created with 046 * the {@link #withFitness(Comparable)}. 047 * 048 * @see Genotype 049 * 050 * @implNote 051 * This class is immutable and thread-safe. 052 * 053 * @param <G> the gene type 054 * @param <C> the fitness result type 055 * 056 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 057 * @since 1.0 058 * @version 6.0 059 */ 060public final class Phenotype< 061 G extends Gene<?, G>, 062 C extends Comparable<? super C> 063> 064 implements 065 Comparable<Phenotype<G, C>>, 066 Verifiable, 067 Serializable 068{ 069 @Serial 070 private static final long serialVersionUID = 6L; 071 072 private final Genotype<G> _genotype; 073 private final long _generation; 074 private final C _fitness; 075 076 /** 077 * Create a new phenotype from the given arguments. 078 * 079 * @param genotype the genotype of this phenotype. 080 * @param generation the current generation of the generated phenotype. 081 * @param fitness the known fitness of the phenotype, maybe {@code null} 082 * @throws NullPointerException if the genotype is {@code null}. 083 * @throws IllegalArgumentException if the given {@code generation} is 084 * {@code < 0}. 085 */ 086 private Phenotype( 087 final Genotype<G> genotype, 088 final long generation, 089 final C fitness 090 ) { 091 if (generation < 0) { 092 throw new IllegalArgumentException(format( 093 "Generation must not < 0 and was %s.", generation 094 )); 095 } 096 097 _genotype = requireNonNull(genotype, "Genotype"); 098 _generation = generation; 099 _fitness = fitness; 100 } 101 102 /** 103 * Applies the given fitness function to the underlying genotype and return 104 * a new phenotype with the (newly) evaluated fitness function, if not 105 * already evaluated. If the fitness value is already set {@code this} 106 * phenotype is returned. 107 * 108 * @since 5.0 109 * 110 * @param ff the fitness function 111 * @return a evaluated phenotype or {@code this} if the fitness value is 112 * already set 113 * @throws NullPointerException if the given fitness function is {@code null} 114 */ 115 public Phenotype<G, C> 116 eval(final Function<? super Genotype<G>, ? extends C> ff) { 117 requireNonNull(ff); 118 return _fitness == null ? withFitness(ff.apply(_genotype)) : this; 119 } 120 121 /** 122 * This method returns a copy of the {@code Genotype}, to guarantee a 123 * immutable class. 124 * 125 * @return the cloned {@code Genotype} of this {@code Phenotype}. 126 * @throws NullPointerException if one of the arguments is {@code null}. 127 */ 128 public Genotype<G> genotype() { 129 return _genotype; 130 } 131 132 /** 133 * A phenotype instance can be created with or without fitness value. 134 * Initially, the phenotype is created without fitness value. The 135 * fitness evaluation strategy is responsible for creating phenotypes with 136 * fitness value assigned. 137 * 138 * @since 4.2 139 * 140 * @see #nonEvaluated() 141 * 142 * @return {@code true} is this phenotype has a fitness value assigned, 143 * {@code false} otherwise 144 */ 145 public boolean isEvaluated() { 146 return _fitness != null; 147 } 148 149 /** 150 * A phenotype instance can be created with or without fitness value. 151 * Initially, the phenotype is created without fitness value. The 152 * fitness evaluation strategy is responsible for creating phenotypes with 153 * fitness value assigned. 154 * 155 * @since 5.0 156 * 157 * @see #isEvaluated() 158 * 159 * @return {@code false} is this phenotype has a fitness value assigned, 160 * {@code true} otherwise 161 */ 162 public boolean nonEvaluated() { 163 return _fitness == null; 164 } 165 166 /** 167 * Return the fitness value of this {@code Phenotype}. 168 * 169 * @see #fitnessOptional() 170 * 171 * @return The fitness value of this {@code Phenotype}. 172 * @throws NoSuchElementException if {@link #isEvaluated()} returns 173 * {@code false} 174 */ 175 public C fitness() { 176 if (_fitness == null) { 177 throw new NoSuchElementException( 178 "Phenotype has no assigned fitness value." 179 ); 180 } 181 182 return _fitness; 183 } 184 185 /** 186 * Return the fitness value of {@code this} phenotype, or 187 * {@link Optional#empty()} if not evaluated yet. 188 * 189 * @since 5.0 190 * 191 * @see #fitness() 192 * 193 * @return the fitness value 194 */ 195 public Optional<C> fitnessOptional() { 196 return Optional.ofNullable(_fitness); 197 } 198 199 /** 200 * Return the generation this {@link Phenotype} was created. 201 * 202 * @see #age(long) 203 * 204 * @return The generation this {@link Phenotype} was created. 205 */ 206 public long generation() { 207 return _generation; 208 } 209 210 /** 211 * Return the age of this phenotype depending on the given current generation. 212 * 213 * @see #generation() 214 * 215 * @param currentGeneration the current generation evaluated by the GA. 216 * @return the age of this phenotype: 217 * {@code currentGeneration - this.getGeneration()}. 218 */ 219 public long age(final long currentGeneration) { 220 return currentGeneration - _generation; 221 } 222 223 /** 224 * Return a phenotype, where the fitness is set to {@code null}. If 225 * {@code this} phenotype isn't evaluated, {@code this} instance is returned. 226 * 227 * @since 6.0 228 * 229 * @return a phenotype, where the fitness is set to {@code null} 230 */ 231 public Phenotype<G, C> nullifyFitness() { 232 return _fitness != null ? of(_genotype, _generation) : this; 233 } 234 235 /** 236 * Test whether this phenotype is valid. The phenotype is valid if its 237 * {@link Genotype} is valid. 238 * 239 * @return true if this phenotype is valid, false otherwise. 240 */ 241 @Override 242 public boolean isValid() { 243 return _genotype.isValid(); 244 } 245 246 @Override 247 public int compareTo(final Phenotype<G, C> pt) { 248 if (isEvaluated()) { 249 return pt.isEvaluated() ? fitness().compareTo(pt.fitness()) : 1; 250 } else { 251 return pt.isEvaluated() ? -1 : 0; 252 } 253 } 254 255 @Override 256 public int hashCode() { 257 return hash(_generation, hash(_fitness, hash(_genotype))); 258 } 259 260 @Override 261 public boolean equals(final Object obj) { 262 return obj instanceof Phenotype<?, ?> other && 263 _generation == other._generation && 264 Objects.equals(_fitness, other._fitness) && 265 Objects.equals(_genotype, other._genotype); 266 } 267 268 @Override 269 public String toString() { 270 return _genotype + " -> " + _fitness; 271 } 272 273 /** 274 * Return a new {@code Phenotype} object with the given <em>raw</em> fitness 275 * value. The returned phenotype is automatically <em>evaluated</em>: 276 * {@code isEvaluated() == true} 277 * 278 * @since 4.2 279 * 280 * @param fitness the phenotypes' fitness value 281 * @throws NullPointerException if the given {@code fitness} value is 282 * {@code null} 283 * @return a new phenotype with the given fitness value 284 */ 285 public Phenotype<G, C> withFitness(final C fitness) { 286 return Phenotype.of( 287 _genotype, 288 _generation, 289 requireNonNull(fitness) 290 ); 291 } 292 293 /** 294 * Return a new {@code Phenotype} object with the given generation. 295 * 296 * @since 5.0 297 * 298 * @param generation the generation of the newly created phenotype 299 * @return a new phenotype with the given generation 300 */ 301 public Phenotype<G, C> withGeneration(final long generation) { 302 return Phenotype.of( 303 _genotype, 304 generation, 305 _fitness 306 ); 307 } 308 309 310 /* ************************************************************************* 311 * Static factory methods. 312 * ************************************************************************/ 313 314 /** 315 * Create a new phenotype from the given arguments. The phenotype is created 316 * with a non-assigned fitness function and the call of {@link #isEvaluated()} 317 * will return {@code false}. 318 * 319 * @param <G> the gene type of the chromosome 320 * @param <C> the fitness value type 321 * @param genotype the genotype of this phenotype. 322 * @param generation the current generation of the generated phenotype. 323 * @return a new phenotype object 324 * @throws NullPointerException if one of the arguments is {@code null}. 325 * @throws IllegalArgumentException if the given {@code generation} is 326 * {@code < 0}. 327 */ 328 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 329 Phenotype<G, C> of(final Genotype<G> genotype, final long generation) { 330 return new Phenotype<>( 331 genotype, 332 generation, 333 null 334 ); 335 } 336 337 /** 338 * Create a new phenotype from the given arguments. 339 * 340 * @param <G> the gene type of the chromosome 341 * @param <C> the fitness value type 342 * @param genotype the genotype of this phenotype. 343 * @param generation the current generation of the generated phenotype. 344 * @param fitness the known fitness of the phenotype. 345 * @return a new phenotype object 346 * @throws NullPointerException if one of the arguments is {@code null}. 347 * @throws IllegalArgumentException if the given {@code generation} is 348 * {@code < 0}. 349 */ 350 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 351 Phenotype<G, C> of( 352 final Genotype<G> genotype, 353 final long generation, 354 final C fitness 355 ) { 356 return new Phenotype<>( 357 genotype, 358 generation, 359 requireNonNull(fitness) 360 ); 361 } 362 363 364 /* ************************************************************************* 365 * Java object serialization 366 * ************************************************************************/ 367 368 @Serial 369 private Object writeReplace() { 370 return new SerialProxy(SerialProxy.PHENOTYPE, this); 371 } 372 373 @Serial 374 private void readObject(final ObjectInputStream stream) 375 throws InvalidObjectException 376 { 377 throw new InvalidObjectException("Serialization proxy required."); 378 } 379 380 void write(final ObjectOutput out) throws IOException { 381 writeLong(_generation, out); 382 out.writeObject(_genotype); 383 out.writeObject(_fitness); 384 } 385 386 @SuppressWarnings({"unchecked", "rawtypes"}) 387 static Object read(final ObjectInput in) 388 throws IOException, ClassNotFoundException 389 { 390 final var generation = readLong(in); 391 final var genotype = (Genotype)in.readObject(); 392 final var fitness = (Comparable)in.readObject(); 393 394 return new Phenotype(genotype, generation, fitness); 395 } 396 397}