001 /*
002 * Java Genetic Algorithm Library (jenetics-4.4.0).
003 * Copyright (c) 2007-2019 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
026 import java.io.IOException;
027 import java.io.ObjectInputStream;
028 import java.io.ObjectOutputStream;
029 import java.io.Serializable;
030 import java.util.Objects;
031 import java.util.function.Function;
032
033 import io.jenetics.internal.util.Lazy;
034 import io.jenetics.internal.util.reflect;
035 import io.jenetics.util.Verifiable;
036
037 /**
038 * The {@code Phenotype} consists of a {@link Genotype} plus a fitness
039 * {@link Function}, where the fitness {@link Function} represents the
040 * environment where the {@link Genotype} lives.
041 * This class implements the {@link Comparable} interface, to define a natural
042 * order between two {@code Phenotype}s. The natural order of the
043 * {@code Phenotypes} is defined by its fitness value (given by the
044 * fitness {@link Function}. The {@code Phenotype} is immutable and therefore
045 * can't be changed after creation.
046 * <p>
047 * The evaluation of the fitness function is performed lazily. Either by calling
048 * one of the fitness accessors ({@link #getFitness()} or {@link #getRawFitness()})
049 * of through the <i>evaluation</i> methods {@link #run()} or {@link #evaluate()}.
050 * Since the {@code Phenotype} implements the {@link Runnable} interface, it is
051 * easily possible to perform the fitness function evaluation concurrently, by
052 * putting it into an {@link java.util.concurrent.ExecutorService}.
053 *
054 * @see Genotype
055 *
056 * @implNote
057 * This class is immutable and thread-safe.
058 *
059 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
060 * @since 1.0
061 * @version 4.2
062 */
063 public final class Phenotype<
064 G extends Gene<?, G>,
065 C extends Comparable<? super C>
066 >
067 implements
068 Comparable<Phenotype<G, C>>,
069 Verifiable,
070 Serializable,
071 Runnable
072 {
073 private static final long serialVersionUID = 5L;
074
075 private final transient Function<? super Genotype<G>, ? extends C> _function;
076 private final transient Function<? super C, ? extends C> _scaler;
077
078 private final Genotype<G> _genotype;
079 private final long _generation;
080
081 private final Lazy<C> _rawFitness;
082 private final Lazy<C> _fitness;
083
084 /**
085 * Create a new phenotype from the given arguments.
086 *
087 * @param genotype the genotype of this phenotype.
088 * @param generation the current generation of the generated phenotype.
089 * @param function the fitness function of this phenotype.
090 * @param scaler the fitness scaler.
091 * @param rawFitness the known raw-fitness of the phenotype, maybe {@code null}
092 * @throws NullPointerException if one of the arguments is {@code null}.
093 * @throws IllegalArgumentException if the given {@code generation} is
094 * {@code < 0}.
095 */
096 private Phenotype(
097 final Genotype<G> genotype,
098 final long generation,
099 final Function<? super Genotype<G>, ? extends C> function,
100 final Function<? super C, ? extends C> scaler,
101 final C rawFitness
102 ) {
103 _genotype = requireNonNull(genotype, "Genotype");
104 _function = requireNonNull(function, "Fitness function");
105 _scaler = requireNonNull(scaler, "Fitness scaler");
106 if (generation < 0) {
107 throw new IllegalArgumentException(format(
108 "Generation must not < 0 and was %s.", generation
109 ));
110 }
111 _generation = generation;
112
113 if (rawFitness != null) {
114 _rawFitness = Lazy.ofValue(rawFitness);
115 _fitness = Lazy.ofValue(scaler.apply(rawFitness));
116 } else {
117 _rawFitness = Lazy.of(() -> _function.apply(_genotype));
118 _fitness = Lazy.of(() -> _scaler.apply(_rawFitness.get()));
119 }
120 }
121
122 /**
123 * This method returns a copy of the {@code Genotype}, to guarantee a
124 * immutable class.
125 *
126 * @return the cloned {@code Genotype} of this {@code Phenotype}.
127 * @throws NullPointerException if one of the arguments is {@code null}.
128 */
129 public Genotype<G> getGenotype() {
130 return _genotype;
131 }
132
133 /**
134 * Evaluates the (raw) fitness values and caches it so the fitness calculation
135 * is performed only once.
136 *
137 * @return this phenotype, for method chaining.
138 */
139 public Phenotype<G, C> evaluate() {
140 getFitness();
141 return this;
142 }
143
144 /**
145 * The fitness value of the <em>phenotype</em> is evaluated lazily. This
146 * method allows to check whether the fitness value has already been
147 * calculated or not.
148 *
149 * @since 4.2
150 *
151 * @return {@code true} is this phenotype has been evaluated, {@code false}
152 * otherwise
153 */
154 public boolean isEvaluated() {
155 return _fitness.isEvaluated();
156 }
157
158 /**
159 * This method simply calls the {@link #evaluate()} method. The purpose of
160 * this method is to have a simple way for concurrent fitness calculation
161 * for expensive fitness values.
162 */
163 @Override
164 public void run() {
165 evaluate();
166 }
167
168 /**
169 * Return the fitness function used by this phenotype to calculate the
170 * (raw) fitness value.
171 *
172 * @return the fitness function.
173 *
174 * @deprecated Will be removed in a later version
175 */
176 @Deprecated
177 public Function<? super Genotype<G>, ? extends C> getFitnessFunction() {
178 return _function;
179 }
180
181 /**
182 * Return the fitness scaler used by this phenotype to scale the <i>raw</i>
183 * fitness.
184 *
185 * @return the fitness scaler.
186 *
187 * @deprecated Will be removed in a later version
188 */
189 @Deprecated
190 public Function<? super C, ? extends C> getFitnessScaler() {
191 return _scaler;
192 }
193
194 /**
195 * Return the fitness value of this {@code Phenotype}.
196 *
197 * @return The fitness value of this {@code Phenotype}.
198 */
199 public C getFitness() {
200 return _fitness.get();
201 }
202
203 /**
204 * Return the raw fitness (before scaling) of the phenotype.
205 *
206 * @return The raw fitness (before scaling) of the phenotype.
207 */
208 public C getRawFitness() {
209 return _rawFitness.get();
210 }
211
212 /**
213 * Return the generation this {@link Phenotype} was created.
214 *
215 * @return The generation this {@link Phenotype} was created.
216 */
217 public long getGeneration() {
218 return _generation;
219 }
220
221 /**
222 * Return the age of this phenotype depending on the given current generation.
223 *
224 * @param currentGeneration the current generation evaluated by the GA.
225 * @return the age of this phenotype:
226 * {@code currentGeneration - this.getGeneration()}.
227 */
228 public long getAge(final long currentGeneration) {
229 return currentGeneration - _generation;
230 }
231
232 /**
233 * Test whether this phenotype is valid. The phenotype is valid if its
234 * {@link Genotype} is valid.
235 *
236 * @return true if this phenotype is valid, false otherwise.
237 */
238 @Override
239 public boolean isValid() {
240 return _genotype.isValid();
241 }
242
243 @Override
244 public int compareTo(final Phenotype<G, C> pt) {
245 return getFitness().compareTo(pt.getFitness());
246 }
247
248 @Override
249 public int hashCode() {
250 return
251 hash(_generation,
252 hash(getFitness(),
253 hash(getRawFitness(),
254 hash(_genotype,
255 hash(getClass())))));
256 }
257
258 @Override
259 public boolean equals(final Object obj) {
260 return obj == this ||
261 obj instanceof Phenotype &&
262 Objects.equals(getFitness(), ((Phenotype)obj).getFitness()) &&
263 Objects.equals(getRawFitness(), ((Phenotype)obj).getRawFitness()) &&
264 Objects.equals(_genotype, ((Phenotype)obj)._genotype) &&
265 _generation == ((Phenotype)obj)._generation;
266 }
267
268 @Override
269 public String toString() {
270 return _genotype + " --> " + getFitness();
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 _function,
290 _scaler,
291 fitness
292 );
293 }
294
295 /**
296 * Create a new {@code Phenotype} with a different {@code Genotype} but the
297 * same {@code generation}, fitness {@code function} and fitness
298 * {@code scaler}.
299 *
300 * @since 3.1
301 *
302 * @param genotype the new genotype
303 * @return a new {@code phenotype} with replaced {@code genotype}
304 * @throws NullPointerException if the given {@code genotype} is {@code null}.
305 *
306 * @deprecated Will be removed without replacement. Is not used.
307 */
308 @Deprecated
309 public Phenotype<G, C> newInstance(final Genotype<G> genotype) {
310 return of(genotype, _generation, _function, _scaler);
311 }
312
313 /**
314 * Factory method for creating a new {@link Phenotype} with the same
315 * {@link Function} and age as this {@link Phenotype}.
316 *
317 * @since 3.5
318 *
319 * @param genotype the new genotype of the new phenotype.
320 * @param generation date of birth (generation) of the new phenotype.
321 * @return New {@link Phenotype} with the same fitness {@link Function}.
322 * @throws NullPointerException if the {@code genotype} is {@code null}.
323 *
324 * @deprecated Will be removed without replacement. No longer needed in the
325 * next major release (5.0).
326 */
327 @Deprecated
328 public Phenotype<G, C> newInstance(
329 final Genotype<G> genotype,
330 final long generation
331 ) {
332 return of(genotype, generation, _function, _scaler);
333 }
334
335 /**
336 * Return a new phenotype with the the genotype of this and with new
337 * fitness function, fitness scaler and generation.
338 *
339 * @param generation the generation of the new phenotype.
340 * @param function the (new) fitness scaler of the created phenotype.
341 * @param scaler the (new) fitness scaler of the created phenotype
342 * @return a new phenotype with the given values.
343 * @throws NullPointerException if one of the values is {@code null}.
344 * @throws IllegalArgumentException if the given {@code generation} is
345 * {@code < 0}.
346 *
347 * @deprecated Will be removed in a later version
348 */
349 @Deprecated
350 public Phenotype<G, C> newInstance(
351 final long generation,
352 final Function<? super Genotype<G>, ? extends C> function,
353 final Function<? super C, ? extends C> scaler
354 ) {
355 return of(_genotype, generation, function, scaler);
356 }
357
358 /**
359 * Return a new phenotype with the the genotype of this and with new
360 * fitness function and generation.
361 *
362 * @param generation the generation of the new phenotype.
363 * @param function the (new) fitness scaler of the created phenotype.
364 * @return a new phenotype with the given values.
365 * @throws NullPointerException if one of the values is {@code null}.
366 * @throws IllegalArgumentException if the given {@code generation} is
367 * {@code < 0}.
368 *
369 * @deprecated Will be removed in a later version
370 */
371 @Deprecated
372 public Phenotype<G, C> newInstance(
373 final long generation,
374 final Function<? super Genotype<G>, ? extends C> function
375 ) {
376 return of(_genotype, generation, function, a -> a);
377 }
378
379 /**
380 * The {@code Genotype} is copied to guarantee an immutable class. Only
381 * the age of the {@code Phenotype} can be incremented.
382 *
383 * @param <G> the gene type of the chromosome
384 * @param <C> the fitness value type
385 * @param genotype the genotype of this phenotype.
386 * @param generation the current generation of the generated phenotype.
387 * @param function the fitness function of this phenotype.
388 * @return a new phenotype from the given parameters
389 * @throws NullPointerException if one of the arguments is {@code null}.
390 * @throws IllegalArgumentException if the given {@code generation} is
391 * {@code < 0}.
392 */
393 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
394 Phenotype<G, C> of(
395 final Genotype<G> genotype,
396 final long generation,
397 final Function<? super Genotype<G>, C> function
398 ) {
399 return Phenotype.<G, C>of(
400 genotype,
401 generation,
402 function,
403 function instanceof Serializable
404 ? (Function<? super C, ? extends C> & Serializable)a -> a
405 : a -> a
406 );
407 }
408
409 /**
410 * Create a new phenotype from the given arguments.
411 *
412 * @param <G> the gene type of the chromosome
413 * @param <C> the fitness value type
414 * @param genotype the genotype of this phenotype.
415 * @param generation the current generation of the generated phenotype.
416 * @param function the fitness function of this phenotype.
417 * @param scaler the fitness scaler.
418 * @return a new phenotype object
419 * @throws NullPointerException if one of the arguments is {@code null}.
420 * @throws IllegalArgumentException if the given {@code generation} is
421 * {@code < 0}.
422 */
423 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
424 Phenotype<G, C> of(
425 final Genotype<G> genotype,
426 final long generation,
427 final Function<? super Genotype<G>, ? extends C> function,
428 final Function<? super C, ? extends C> scaler
429 ) {
430 return new Phenotype<>(
431 genotype,
432 generation,
433 function,
434 scaler,
435 null
436 );
437 }
438
439 /**
440 * Create a new phenotype from the given arguments. This factory method is
441 * used when the fitness value of the phenotype has been calculated by a
442 * different {@link io.jenetics.engine.Engine.GenotypeEvaluator} strategy
443 * then the default one.
444 *
445 * @param <G> the gene type of the chromosome
446 * @param <C> the fitness value type
447 * @param genotype the genotype of this phenotype.
448 * @param generation the current generation of the generated phenotype.
449 * @param function the fitness function of this phenotype.
450 * @param scaler the fitness scaler.
451 * @param rawFitness the known raw-fitness of the phenotype.
452 * @return a new phenotype object
453 * @throws NullPointerException if one of the arguments is {@code null}.
454 * @throws IllegalArgumentException if the given {@code generation} is
455 * {@code < 0}.
456 */
457 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
458 Phenotype<G, C> of(
459 final Genotype<G> genotype,
460 final long generation,
461 final Function<? super Genotype<G>, ? extends C> function,
462 final Function<? super C, ? extends C> scaler,
463 final C rawFitness
464 ) {
465 return new Phenotype<>(
466 genotype,
467 generation,
468 function,
469 scaler,
470 requireNonNull(rawFitness)
471 );
472 }
473
474
475 /* *************************************************************************
476 * Java object serialization
477 * ************************************************************************/
478
479 private void writeObject(final ObjectOutputStream out)
480 throws IOException
481 {
482 out.defaultWriteObject();
483 out.writeLong(getGeneration());
484 out.writeObject(getGenotype());
485 out.writeObject(getFitness());
486 out.writeObject(getRawFitness());
487 }
488
489 private void readObject(final ObjectInputStream in)
490 throws IOException, ClassNotFoundException
491 {
492 in.defaultReadObject();
493 reflect.setField(this, "_generation", in.readLong());
494 reflect.setField(this, "_genotype", in.readObject());
495 reflect.setField(this, "_fitness", Lazy.ofValue(in.readObject()));
496 reflect.setField(this, "_rawFitness", Lazy.ofValue(in.readObject()));
497
498 reflect.setField(this, "_function", Function.identity());
499 reflect.setField(this, "_scaler", Function.identity());
500 }
501
502 }
|