001/*
002 * Java Genetic Algorithm Library (jenetics-8.1.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 */
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 an 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}