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