001 /*
002 * Java Genetic Algorithm Library (jenetics-6.3.0).
003 * Copyright (c) 2007-2021 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 G extends Gene<?, G>,
061 C 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, _generation) : this;
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() ? -1 : 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 out) throws 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 }
|