Phenotype.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-6.0.0).
003  * Copyright (c) 2007-2020 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.Serializable;
034 import java.util.NoSuchElementException;
035 import java.util.Objects;
036 import java.util.Optional;
037 import java.util.function.Function;
038 
039 import 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  */
059 public final class Phenotype<
060     extends Gene<?, G>,
061     extends Comparable<? super C>
062 >
063     implements
064         Comparable<Phenotype<G, C>>,
065         Verifiable,
066         Serializable
067 {
068     private static final long serialVersionUID = 6L;
069 
070     private final Genotype<G> _genotype;
071     private final long _generation;
072     private final C _fitness;
073 
074     /**
075      * Create a new phenotype from the given arguments.
076      *
077      @param genotype the genotype of this phenotype.
078      @param generation the current generation of the generated phenotype.
079      @param fitness the known fitness of the phenotype, maybe {@code null}
080      @throws NullPointerException if the genotype is {@code null}.
081      @throws IllegalArgumentException if the given {@code generation} is
082      *         {@code < 0}.
083      */
084     private Phenotype(
085         final Genotype<G> genotype,
086         final long generation,
087         final C fitness
088     ) {
089         if (generation < 0) {
090             throw new IllegalArgumentException(format(
091                 "Generation must not < 0 and was %s.", generation
092             ));
093         }
094 
095         _genotype = requireNonNull(genotype, "Genotype");
096         _generation = generation;
097         _fitness = fitness;
098     }
099 
100     /**
101      * Applies the given fitness function to the underlying genotype and return
102      * a new phenotype with the (newly) evaluated fitness function, if not
103      * already evaluated. If the fitness value is already set {@code this}
104      * phenotype is returned.
105      *
106      @since 5.0
107      *
108      @param ff the fitness function
109      @return a evaluated phenotype or {@code this} if the fitness value is
110      *         already set
111      @throws NullPointerException if the given fitness function is {@code null}
112      */
113     public Phenotype<G, C>
114     eval(final Function<? super Genotype<G>, ? extends C> ff) {
115         requireNonNull(ff);
116         return _fitness == null ? withFitness(ff.apply(_genotype)) this;
117     }
118 
119     /**
120      * This method returns a copy of the {@code Genotype}, to guarantee a
121      * immutable class.
122      *
123      @return the cloned {@code Genotype} of this {@code Phenotype}.
124      @throws NullPointerException if one of the arguments is {@code null}.
125      */
126     public Genotype<G> genotype() {
127         return _genotype;
128     }
129 
130     /**
131      * A phenotype instance can be created with or without fitness value.
132      * Initially, the phenotype is created without fitness value. The
133      * fitness evaluation strategy is responsible for creating phenotypes with
134      * fitness value assigned.
135      *
136      @since 4.2
137      *
138      @see #nonEvaluated()
139      *
140      @return {@code true} is this phenotype has an fitness value assigned,
141      *         {@code false} otherwise
142      */
143     public boolean isEvaluated() {
144         return _fitness != null;
145     }
146 
147     /**
148      * A phenotype instance can be created with or without fitness value.
149      * Initially, the phenotype is created without fitness value. The
150      * fitness evaluation strategy is responsible for creating phenotypes with
151      * fitness value assigned.
152      *
153      @since 5.0
154      *
155      @see #isEvaluated()
156      *
157      @return {@code false} is this phenotype has an fitness value assigned,
158      *         {@code true} otherwise
159      */
160     public boolean nonEvaluated() {
161         return _fitness == null;
162     }
163 
164     /**
165      * Return the fitness value of this {@code Phenotype}.
166      *
167      @see #fitnessOptional()
168      *
169      @return The fitness value of this {@code Phenotype}.
170      @throws NoSuchElementException if {@link #isEvaluated()} returns
171      *         {@code false}
172      */
173     public C fitness() {
174         if (_fitness == null) {
175             throw new NoSuchElementException(
176                 "Phenotype has no assigned fitness value."
177             );
178         }
179 
180         return _fitness;
181     }
182 
183     /**
184      * Return the fitness value of {@code this} phenotype, or
185      {@link Optional#empty()} if not evaluated yet.
186      *
187      @since 5.0
188      *
189      @see #fitness()
190      *
191      @return the fitness value
192      */
193     public Optional<C> fitnessOptional() {
194         return Optional.ofNullable(_fitness);
195     }
196 
197     /**
198      * Return the generation this {@link Phenotype} was created.
199      *
200      @see #age(long)
201      *
202      @return The generation this {@link Phenotype} was created.
203      */
204     public long generation() {
205         return _generation;
206     }
207 
208     /**
209      * Return the age of this phenotype depending on the given current generation.
210      *
211      @see #generation()
212      *
213      @param currentGeneration the current generation evaluated by the GA.
214      @return the age of this phenotype:
215      *          {@code currentGeneration - this.getGeneration()}.
216      */
217     public long age(final long currentGeneration) {
218         return currentGeneration - _generation;
219     }
220 
221     /**
222      * Return a phenotype, where the fitness is set to {@code null}. If
223      * {@code this} phenotype isn't evaluated, {@code this} instance is returned.
224      *
225      @since 6.0
226      *
227      @return a phenotype, where the fitness is set to {@code null}
228      */
229     public Phenotype<G, C> nullifyFitness() {
230         return _fitness != null ? of(_genotype, _generationthis;
231     }
232 
233     /**
234      * Test whether this phenotype is valid. The phenotype is valid if its
235      {@link Genotype} is valid.
236      *
237      @return true if this phenotype is valid, false otherwise.
238      */
239     @Override
240     public boolean isValid() {
241         return _genotype.isValid();
242     }
243 
244     @Override
245     public int compareTo(final Phenotype<G, C> pt) {
246         if (isEvaluated()) {
247             return pt.isEvaluated() ? fitness().compareTo(pt.fitness()) 1;
248         else {
249             return pt.isEvaluated() ? -0;
250         }
251     }
252 
253     @Override
254     public int hashCode() {
255         return hash(_generation, hash(_fitness, hash(_genotype)));
256     }
257 
258     @Override
259     public boolean equals(final Object obj) {
260         return obj == this ||
261             obj instanceof Phenotype &&
262             _generation == ((Phenotype)obj)._generation &&
263             Objects.equals(_fitness, ((Phenotype)obj)._fitness&&
264             Objects.equals(_genotype, ((Phenotype)obj)._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     private Object writeReplace() {
368         return new Serial(Serial.PHENOTYPE, this);
369     }
370 
371     private void readObject(final ObjectInputStream stream)
372         throws InvalidObjectException
373     {
374         throw new InvalidObjectException("Serialization proxy required.");
375     }
376 
377     void write(final ObjectOutput outthrows IOException {
378         writeLong(_generation, out);
379         out.writeObject(_genotype);
380         out.writeObject(_fitness);
381     }
382 
383     @SuppressWarnings({"unchecked""rawtypes"})
384     static Object read(final ObjectInput in)
385         throws IOException, ClassNotFoundException
386     {
387         final var generation = readLong(in);
388         final var genotype = (Genotype)in.readObject();
389         final var fitness = (Comparable)in.readObject();
390 
391         return new Phenotype(
392             genotype,
393             generation,
394             fitness
395         );
396     }
397 
398 }