Phenotype.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-8.0.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  */
020 package io.jenetics;
021 
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 import static io.jenetics.internal.util.Hashes.hash;
025 import static io.jenetics.internal.util.SerialIO.readLong;
026 import static io.jenetics.internal.util.SerialIO.writeLong;
027 
028 import java.io.IOException;
029 import java.io.InvalidObjectException;
030 import java.io.ObjectInput;
031 import java.io.ObjectInputStream;
032 import java.io.ObjectOutput;
033 import java.io.Serial;
034 import java.io.Serializable;
035 import java.util.NoSuchElementException;
036 import java.util.Objects;
037 import java.util.Optional;
038 import java.util.function.Function;
039 
040 import 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  */
060 public final class Phenotype<
061     extends Gene<?, G>,
062     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, _generationthis;
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() ? -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 outthrows 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 }