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}