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 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 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, _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 }
|