001 /*
002 * Java Genetic Algorithm Library (jenetics-5.2.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 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
053 * @since 1.0
054 * @version 5.2
055 */
056 public final class Phenotype<
057 G extends Gene<?, G>,
058 C extends Comparable<? super C>
059 >
060 implements
061 Comparable<Phenotype<G, C>>,
062 Verifiable,
063 Serializable
064 {
065 private static final long serialVersionUID = 6L;
066
067 private final Genotype<G> _genotype;
068 private final long _generation;
069 private final C _fitness;
070
071 /**
072 * Create a new phenotype from the given arguments.
073 *
074 * @param genotype the genotype of this phenotype.
075 * @param generation the current generation of the generated phenotype.
076 * @param fitness the known fitness of the phenotype, maybe {@code null}
077 * @throws NullPointerException if the genotype is {@code null}.
078 * @throws IllegalArgumentException if the given {@code generation} is
079 * {@code < 0}.
080 */
081 private Phenotype(
082 final Genotype<G> genotype,
083 final long generation,
084 final C fitness
085 ) {
086 if (generation < 0) {
087 throw new IllegalArgumentException(format(
088 "Generation must not < 0 and was %s.", generation
089 ));
090 }
091
092 _genotype = requireNonNull(genotype, "Genotype");
093 _generation = generation;
094 _fitness = fitness;
095 }
096
097 /**
098 * Applies the given fitness function to the underlying genotype and return
099 * a new phenotype with the (newly) evaluated fitness function, if not
100 * already evaluated. If the fitness value is already set {@code this}
101 * phenotype is returned.
102 *
103 * @since 5.0
104 *
105 * @param ff the fitness function
106 * @return a evaluated phenotype or {@code this} if the fitness value is
107 * already set
108 * @throws NullPointerException if the given fitness function is {@code null}
109 */
110 public Phenotype<G, C>
111 eval(final Function<? super Genotype<G>, ? extends C> ff) {
112 requireNonNull(ff);
113 return _fitness == null ? withFitness(ff.apply(_genotype)) : this;
114 }
115
116 /**
117 * This method returns a copy of the {@code Genotype}, to guarantee a
118 * immutable class.
119 *
120 * @return the cloned {@code Genotype} of this {@code Phenotype}.
121 * @throws NullPointerException if one of the arguments is {@code null}.
122 */
123 public Genotype<G> genotype() {
124 return _genotype;
125 }
126
127 /**
128 * This method returns a copy of the {@code Genotype}, to guarantee a
129 * immutable class.
130 *
131 * @return the cloned {@code Genotype} of this {@code Phenotype}.
132 * @throws NullPointerException if one of the arguments is {@code null}.
133 * @deprecated Use {@link #genotype()} instead
134 */
135 @Deprecated
136 public Genotype<G> getGenotype() {
137 return _genotype;
138 }
139
140 /**
141 * A phenotype instance can be created with or without fitness value.
142 * Initially, the phenotype is created without fitness value. The
143 * fitness evaluation strategy is responsible for creating phenotypes with
144 * fitness value assigned.
145 *
146 * @since 4.2
147 *
148 * @see #nonEvaluated()
149 *
150 * @return {@code true} is this phenotype has an fitness value assigned,
151 * {@code false} otherwise
152 */
153 public boolean isEvaluated() {
154 return _fitness != null;
155 }
156
157 /**
158 * A phenotype instance can be created with or without fitness value.
159 * Initially, the phenotype is created without fitness value. The
160 * fitness evaluation strategy is responsible for creating phenotypes with
161 * fitness value assigned.
162 *
163 * @since 5.0
164 *
165 * @see #isEvaluated()
166 *
167 * @return {@code false} is this phenotype has an fitness value assigned,
168 * {@code true} otherwise
169 */
170 public boolean nonEvaluated() {
171 return _fitness == null;
172 }
173
174 /**
175 * Return the fitness value of this {@code Phenotype}.
176 *
177 * @see #fitnessOptional()
178 *
179 * @return The fitness value of this {@code Phenotype}.
180 * @throws NoSuchElementException if {@link #isEvaluated()} returns
181 * {@code false}
182 */
183 public C fitness() {
184 if (_fitness == null) {
185 throw new NoSuchElementException(
186 "Phenotype has no assigned fitness value."
187 );
188 }
189
190 return _fitness;
191 }
192
193 /**
194 * Return the fitness value of this {@code Phenotype}.
195 *
196 * @see #fitnessOptional()
197 *
198 * @return The fitness value of this {@code Phenotype}.
199 * @throws NoSuchElementException if {@link #isEvaluated()} returns
200 * {@code false}
201 * @deprecated Use {@link #fitness()} instead
202 */
203 @Deprecated
204 public C getFitness() {
205 return fitness();
206 }
207
208 /**
209 * Return the fitness value of {@code this} phenotype, or
210 * {@link Optional#empty()} if not evaluated yet.
211 *
212 * @since 5.0
213 *
214 * @see #fitness()
215 *
216 * @return the fitness value
217 */
218 public Optional<C> fitnessOptional() {
219 return Optional.ofNullable(_fitness);
220 }
221
222 /**
223 * Return the generation this {@link Phenotype} was created.
224 *
225 * @see #age(long)
226 *
227 * @return The generation this {@link Phenotype} was created.
228 */
229 public long generation() {
230 return _generation;
231 }
232
233 /**
234 * Return the generation this {@link Phenotype} was created.
235 *
236 * @see #age(long)
237 *
238 * @return The generation this {@link Phenotype} was created.
239 * @deprecated Use {@link #generation()} instead
240 */
241 @Deprecated
242 public long getGeneration() {
243 return _generation;
244 }
245
246 /**
247 * Return the age of this phenotype depending on the given current generation.
248 *
249 * @see #generation()
250 *
251 * @param currentGeneration the current generation evaluated by the GA.
252 * @return the age of this phenotype:
253 * {@code currentGeneration - this.getGeneration()}.
254 */
255 public long age(final long currentGeneration) {
256 return currentGeneration - _generation;
257 }
258
259 /**
260 * Return the age of this phenotype depending on the given current generation.
261 *
262 * @see #generation()
263 *
264 * @param currentGeneration the current generation evaluated by the GA.
265 * @return the age of this phenotype:
266 * {@code currentGeneration - this.getGeneration()}.
267 * @deprecated Use {@link #age(long)} instead
268 */
269 @Deprecated
270 public long getAge(final long currentGeneration) {
271 return age(currentGeneration);
272 }
273
274 /**
275 * Test whether this phenotype is valid. The phenotype is valid if its
276 * {@link Genotype} is valid.
277 *
278 * @return true if this phenotype is valid, false otherwise.
279 */
280 @Override
281 public boolean isValid() {
282 return _genotype.isValid();
283 }
284
285 @Override
286 public int compareTo(final Phenotype<G, C> pt) {
287 if (isEvaluated()) {
288 return pt.isEvaluated() ? fitness().compareTo(pt.fitness()) : 1;
289 } else {
290 return pt.isEvaluated() ? -1 : 0;
291 }
292 }
293
294 @Override
295 public int hashCode() {
296 return hash(_generation, hash(_fitness, hash(_genotype)));
297 }
298
299 @Override
300 public boolean equals(final Object obj) {
301 return obj == this ||
302 obj instanceof Phenotype &&
303 _generation == ((Phenotype)obj)._generation &&
304 Objects.equals(_fitness, ((Phenotype)obj)._fitness) &&
305 Objects.equals(_genotype, ((Phenotype)obj)._genotype);
306 }
307
308 @Override
309 public String toString() {
310 return _genotype + " -> " + _fitness;
311 }
312
313 /**
314 * Return a new {@code Phenotype} object with the given <em>raw</em> fitness
315 * value. The returned phenotype is automatically <em>evaluated</em>:
316 * {@code isEvaluated() == true}
317 *
318 * @since 4.2
319 *
320 * @param fitness the phenotypes fitness value
321 * @throws NullPointerException if the given {@code fitness} value is
322 * {@code null}
323 * @return a new phenotype with the given fitness value
324 */
325 public Phenotype<G, C> withFitness(final C fitness) {
326 return Phenotype.of(
327 _genotype,
328 _generation,
329 requireNonNull(fitness)
330 );
331 }
332
333 /**
334 * Return a new {@code Phenotype} object with the given generation.
335 *
336 * @since 5.0
337 *
338 * @param generation the generation of the newly created phenotype
339 * @return a new phenotype with the given generation
340 */
341 public Phenotype<G, C> withGeneration(final long generation) {
342 return Phenotype.of(
343 _genotype,
344 generation,
345 _fitness
346 );
347 }
348
349
350 /* *************************************************************************
351 * Static factory methods.
352 * ************************************************************************/
353
354 /**
355 * Create a new phenotype from the given arguments. The phenotype is created
356 * with a non assigned fitness function and the call of {@link #isEvaluated()}
357 * will return {@code false}.
358 *
359 * @param <G> the gene type of the chromosome
360 * @param <C> the fitness value type
361 * @param genotype the genotype of this phenotype.
362 * @param generation the current generation of the generated phenotype.
363 * @return a new phenotype object
364 * @throws NullPointerException if one of the arguments is {@code null}.
365 * @throws IllegalArgumentException if the given {@code generation} is
366 * {@code < 0}.
367 */
368 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
369 Phenotype<G, C> of(final Genotype<G> genotype, final long generation) {
370 return new Phenotype<>(
371 genotype,
372 generation,
373 null
374 );
375 }
376
377 /**
378 * Create a new phenotype from the given arguments.
379 *
380 * @param <G> the gene type of the chromosome
381 * @param <C> the fitness value type
382 * @param genotype the genotype of this phenotype.
383 * @param generation the current generation of the generated phenotype.
384 * @param fitness the known fitness of the phenotype.
385 * @return a new phenotype object
386 * @throws NullPointerException if one of the arguments is {@code null}.
387 * @throws IllegalArgumentException if the given {@code generation} is
388 * {@code < 0}.
389 */
390 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
391 Phenotype<G, C> of(
392 final Genotype<G> genotype,
393 final long generation,
394 final C fitness
395 ) {
396 return new Phenotype<>(
397 genotype,
398 generation,
399 requireNonNull(fitness)
400 );
401 }
402
403
404 /* *************************************************************************
405 * Java object serialization
406 * ************************************************************************/
407
408 private Object writeReplace() {
409 return new Serial(Serial.PHENOTYPE, this);
410 }
411
412 private void readObject(final ObjectInputStream stream)
413 throws InvalidObjectException
414 {
415 throw new InvalidObjectException("Serialization proxy required.");
416 }
417
418 void write(final ObjectOutput out) throws IOException {
419 writeLong(_generation, out);
420 out.writeObject(_genotype);
421 out.writeObject(_fitness);
422 }
423
424 @SuppressWarnings({"unchecked", "rawtypes"})
425 static Object read(final ObjectInput in)
426 throws IOException, ClassNotFoundException
427 {
428 final long generation = readLong(in);
429 final Genotype genotype = (Genotype)in.readObject();
430 final Comparable fitness = (Comparable)in.readObject();
431
432 return new Phenotype(
433 genotype,
434 generation,
435 fitness
436 );
437 }
438
439 }
|