001 /*
002 * Java Genetic Algorithm Library (jenetics-3.9.0).
003 * Copyright (c) 2007-2017 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@gmx.at)
019 */
020 package org.jenetics.engine;
021
022 import static java.util.Objects.requireNonNull;
023
024 import java.io.Serializable;
025 import java.util.List;
026 import java.util.Objects;
027 import java.util.function.Function;
028 import java.util.stream.Collector;
029 import java.util.stream.Collectors;
030
031 import org.jenetics.internal.util.Lazy;
032
033 import org.jenetics.Gene;
034 import org.jenetics.Genotype;
035 import org.jenetics.Optimize;
036 import org.jenetics.Phenotype;
037 import org.jenetics.Population;
038 import org.jenetics.stat.MinMax;
039
040 /**
041 * Represents a state of the GA after an evolution step. It also represents the
042 * final state of an evolution process and can be created with an appropriate
043 * collector:
044 * <pre>{@code
045 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = ...;
046 * final EvolutionResult<EnumGene<Point>, Double> result = Engine.builder(tsm)
047 * .optimize(Optimize.MINIMUM).build()
048 * .stream()
049 * .limit(100)
050 * .collect(EvolutionResult.toBestEvolutionResult());
051 * }</pre>
052 *
053 * @see EvolutionStart
054 * @see Engine
055 *
056 * @param <G> the gene type
057 * @param <C> the fitness type
058 *
059 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
060 * @since 3.0
061 * @version 3.9
062 */
063 public final class EvolutionResult<
064 G extends Gene<?, G>,
065 C extends Comparable<? super C>
066 >
067 implements Comparable<EvolutionResult<G, C>>, Serializable
068 {
069 private static final long serialVersionUID = 1L;
070
071 private final Optimize _optimize;
072 private final Population<G, C> _population;
073 private final long _generation;
074 private final long _totalGenerations;
075
076 private final EvolutionDurations _durations;
077 private final int _killCount;
078 private final int _invalidCount;
079 private final int _alterCount;
080
081 private final Lazy<Phenotype<G, C>> _best;
082 private final Lazy<Phenotype<G, C>> _worst;
083
084 private EvolutionResult(
085 final Optimize optimize,
086 final Population<G, C> population,
087 final long generation,
088 final long totalGenerations,
089 final EvolutionDurations durations,
090 final int killCount,
091 final int invalidCount,
092 final int alterCount
093 ) {
094 _optimize = requireNonNull(optimize);
095 _population = requireNonNull(population).copy();
096 _generation = generation;
097 _totalGenerations = totalGenerations;
098 _durations = requireNonNull(durations);
099 _killCount = killCount;
100 _invalidCount = invalidCount;
101 _alterCount = alterCount;
102
103 _best = Lazy.of(() -> _population.stream()
104 .max(_optimize.ascending())
105 .orElse(null)
106 );
107
108 _worst = Lazy.of(() -> _population.stream()
109 .min(_optimize.ascending())
110 .orElse(null)
111 );
112 }
113
114 /**
115 * Return the optimization strategy used.
116 *
117 * @return the optimization strategy used
118 */
119 public Optimize getOptimize() {
120 return _optimize;
121 }
122
123 /**
124 * Return the population after the evolution step.
125 *
126 * @return the population after the evolution step
127 */
128 public Population<G, C> getPopulation() {
129 return _population.copy();
130 }
131
132 /**
133 * Return the current list of genotypes of this evolution result.
134 *
135 * @since 3.9
136 *
137 * @return the list of genotypes of this evolution result.
138 */
139 public List<Genotype<G>> getGenotypes() {
140 return _population.stream()
141 .map(Phenotype::getGenotype)
142 .collect(Collectors.toList());
143 }
144
145 /**
146 * The current generation.
147 *
148 * @return the current generation
149 */
150 public long getGeneration() {
151 return _generation;
152 }
153
154 /**
155 * Return the generation count evaluated so far.
156 *
157 * @return the total number of generations evaluated so far
158 */
159 public long getTotalGenerations() {
160 return _totalGenerations;
161 }
162
163 /**
164 * Return the timing (meta) information of the evolution step.
165 *
166 * @return the timing (meta) information of the evolution step
167 */
168 public EvolutionDurations getDurations() {
169 return _durations;
170 }
171
172 /**
173 * Return the number of killed individuals.
174 *
175 * @return the number of killed individuals
176 */
177 public int getKillCount() {
178 return _killCount;
179 }
180
181 /**
182 * Return the number of invalid individuals.
183 *
184 * @return the number of invalid individuals
185 */
186 public int getInvalidCount() {
187 return _invalidCount;
188 }
189
190 /**
191 * The number of altered individuals.
192 *
193 * @return the number of altered individuals
194 */
195 public int getAlterCount() {
196 return _alterCount;
197 }
198
199 /**
200 * Return the best {@code Phenotype} of the result population.
201 *
202 * @return the best {@code Phenotype} of the result population
203 */
204 public Phenotype<G, C> getBestPhenotype() {
205 return _best.get();
206 }
207
208 /**
209 * Return the worst {@code Phenotype} of the result population.
210 *
211 * @return the worst {@code Phenotype} of the result population
212 */
213 public Phenotype<G, C> getWorstPhenotype() {
214 return _worst.get();
215 }
216
217 /**
218 * Return the best population fitness.
219 *
220 * @return The best population fitness.
221 */
222 public C getBestFitness() {
223 return _best.get() != null ? _best.get().getFitness() : null;
224 }
225
226 /**
227 * Return the worst population fitness.
228 *
229 * @return The worst population fitness.
230 */
231 public C getWorstFitness() {
232 return _worst.get() != null ? _worst.get().getFitness() : null;
233 }
234
235 /**
236 * Return the next evolution start object with the current population and
237 * the incremented generation.
238 *
239 * @return the next evolution start object
240 */
241 EvolutionStart<G, C> next() {
242 return EvolutionStart.of(_population, _generation + 1);
243 }
244
245 /**
246 * Compare {@code this} evolution result with another one, according the
247 * populations best individual.
248 *
249 * @param other the other evolution result to compare
250 * @return a negative integer, zero, or a positive integer as this result
251 * is less than, equal to, or greater than the specified result.
252 */
253 @Override
254 public int compareTo(final EvolutionResult<G, C> other) {
255 return _optimize.compare(_best.get(), other._best.get());
256 }
257
258 private EvolutionResult<G, C> withTotalGenerations(final long total) {
259 return of(
260 _optimize,
261 _population,
262 _generation,
263 total,
264 _durations,
265 _killCount,
266 _invalidCount,
267 _alterCount
268 );
269 }
270
271 @Override
272 public int hashCode() {
273 int hash = 17;
274 hash += 31*Objects.hashCode(_optimize) + 17;
275 hash += 31*Objects.hashCode(_population) + 17;
276 hash += 31*Objects.hashCode(_generation) + 17;
277 hash += 31*Objects.hashCode(_totalGenerations) + 17;
278 hash += 31*Objects.hashCode(_durations) + 17;
279 hash += 31*Objects.hashCode(_killCount) + 17;
280 hash += 31*Objects.hashCode(_invalidCount) + 17;
281 hash += 31*Objects.hashCode(_alterCount) + 17;
282 hash += 31*Objects.hashCode(getBestFitness()) + 17;
283 return hash;
284 }
285
286 @Override
287 public boolean equals(final Object obj) {
288 return obj instanceof EvolutionResult<?, ?> &&
289 Objects.equals(_optimize,
290 ((EvolutionResult<?, ?>)obj)._optimize) &&
291 Objects.equals(_population,
292 ((EvolutionResult<?, ?>)obj)._population) &&
293 Objects.equals(_generation,
294 ((EvolutionResult<?, ?>)obj)._generation) &&
295 Objects.equals(_totalGenerations,
296 ((EvolutionResult<?, ?>)obj)._totalGenerations) &&
297 Objects.equals(_durations,
298 ((EvolutionResult<?, ?>)obj)._durations) &&
299 Objects.equals(_killCount,
300 ((EvolutionResult<?, ?>)obj)._killCount) &&
301 Objects.equals(_invalidCount,
302 ((EvolutionResult<?, ?>)obj)._invalidCount) &&
303 Objects.equals(_alterCount,
304 ((EvolutionResult<?, ?>)obj)._alterCount) &&
305 Objects.equals(getBestFitness(),
306 ((EvolutionResult<?, ?>)obj).getBestFitness());
307 }
308
309
310 /* *************************************************************************
311 * Some static collector/factory methods.
312 * ************************************************************************/
313
314 /**
315 * Return a collector which collects the best result of an evolution stream.
316 *
317 * <pre>{@code
318 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = ...;
319 * final EvolutionResult<EnumGene<Point>, Double> result = Engine.builder(tsm)
320 * .optimize(Optimize.MINIMUM).build()
321 * .stream()
322 * .limit(100)
323 * .collect(EvolutionResult.toBestEvolutionResult());
324 * }</pre>
325 *
326 * If the collected {@link EvolutionStream} is empty, the collector returns
327 * <b>{@code null}</b>.
328 *
329 * @param <G> the gene type
330 * @param <C> the fitness type
331 * @return a collector which collects the best result of an evolution stream
332 */
333 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
334 Collector<EvolutionResult<G, C>, ?, EvolutionResult<G, C>>
335 toBestEvolutionResult() {
336 return Collector.of(
337 MinMax::<EvolutionResult<G, C>>of,
338 MinMax::accept,
339 MinMax::combine,
340 mm -> mm.getMax() != null
341 ? mm.getMax().withTotalGenerations(mm.getCount())
342 : null
343 );
344 }
345
346 /**
347 * Return a collector which collects the best phenotype of an evolution
348 * stream.
349 *
350 * <pre>{@code
351 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = ...;
352 * final Phenotype<EnumGene<Point>, Double> result = Engine.builder(tsm)
353 * .optimize(Optimize.MINIMUM).build()
354 * .stream()
355 * .limit(100)
356 * .collect(EvolutionResult.toBestPhenotype());
357 * }</pre>
358 *
359 * If the collected {@link EvolutionStream} is empty, the collector returns
360 * <b>{@code null}</b>.
361 *
362 * @param <G> the gene type
363 * @param <C> the fitness type
364 * @return a collector which collects the best phenotype of an evolution
365 * stream
366 */
367 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
368 Collector<EvolutionResult<G, C>, ?, Phenotype<G, C>>
369 toBestPhenotype() {
370 return Collector.of(
371 MinMax::<EvolutionResult<G, C>>of,
372 MinMax::accept,
373 MinMax::combine,
374 mm -> mm.getMax() != null
375 ? mm.getMax().getBestPhenotype()
376 : null
377 );
378 }
379
380 /**
381 * Return a collector which collects the best genotype of an evolution
382 * stream.
383 *
384 * <pre>{@code
385 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = ...;
386 * final Genotype<EnumGene<Point>> result = Engine.builder(tsm)
387 * .optimize(Optimize.MINIMUM).build()
388 * .stream()
389 * .limit(100)
390 * .collect(EvolutionResult.toBestGenotype());
391 * }</pre>
392 *
393 * If the collected {@link EvolutionStream} is empty, the collector returns
394 * <b>{@code null}</b>.
395 *
396 * @param <G> the gene type
397 * @param <C> the fitness type
398 * @return a collector which collects the best genotype of an evolution
399 * stream
400 */
401 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
402 Collector<EvolutionResult<G, C>, ?, Genotype<G>>
403 toBestGenotype() {
404 return Collector.of(
405 MinMax::<EvolutionResult<G, C>>of,
406 MinMax::accept,
407 MinMax::combine,
408 mm -> mm.getMax() != null
409 ? mm.getMax().getBestPhenotype() != null
410 ? mm.getMax().getBestPhenotype().getGenotype()
411 : null
412 : null
413 );
414 }
415
416 /**
417 * Return a collector which collects the best <em>result</em> (in the native
418 * problem space).
419 *
420 * <pre>{@code
421 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = ...;
422 * final ISeq<Point> route = Engine.builder(tsm)
423 * .optimize(Optimize.MINIMUM).build()
424 * .stream()
425 * .limit(100)
426 * .collect(EvolutionResult.toBestResult(tsm.codec().decoder()));
427 * }</pre>
428 *
429 * If the collected {@link EvolutionStream} is empty, the collector returns
430 * <b>{@code null}</b>.
431 *
432 * @since 3.6
433 *
434 * @param decoder the decoder which converts the {@code Genotype} into the
435 * result of the problem space.
436 * @param <T> the <em>native</em> problem result type
437 * @param <G> the gene type
438 * @param <C> the fitness result type
439 * @return a collector which collects the best result of an evolution stream
440 * @throws NullPointerException if the given {@code decoder} is {@code null}
441 */
442 public static <G extends Gene<?, G>, C extends Comparable<? super C>, T>
443 Collector<EvolutionResult<G, C>, ?, T>
444 toBestResult(final Function<Genotype<G>, T> decoder) {
445 requireNonNull(decoder);
446
447 return Collector.of(
448 MinMax::<EvolutionResult<G, C>>of,
449 MinMax::accept,
450 MinMax::combine,
451 mm -> mm.getMax() != null
452 ? mm.getMax().getBestPhenotype() != null
453 ? decoder.apply(mm.getMax().getBestPhenotype().getGenotype())
454 : null
455 : null
456 );
457 }
458
459 /**
460 * Return a collector which collects the best <em>result</em> (in the native
461 * problem space).
462 *
463 * <pre>{@code
464 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = ...;
465 * final ISeq<Point> route = Engine.builder(tsm)
466 * .optimize(Optimize.MINIMUM).build()
467 * .stream()
468 * .limit(100)
469 * .collect(EvolutionResult.toBestResult(tsm.codec()));
470 * }</pre>
471 *
472 * If the collected {@link EvolutionStream} is empty, the collector returns
473 * <b>{@code null}</b>.
474 *
475 * @since 3.6
476 *
477 * @param codec the problem decoder
478 * @param <T> the <em>native</em> problem result type
479 * @param <G> the gene type
480 * @param <C> the fitness result type
481 * @return a collector which collects the best result of an evolution stream
482 * @throws NullPointerException if the given {@code codec} is {@code null}
483 */
484 public static <G extends Gene<?, G>, C extends Comparable<? super C>, T>
485 Collector<EvolutionResult<G, C>, ?, T>
486 toBestResult(final Codec<T, G> codec) {
487 return toBestResult(codec.decoder());
488 }
489
490 /**
491 * Return an new {@code EvolutionResult} object with the given values.
492 *
493 * @param optimize the optimization strategy used
494 * @param population the population after the evolution step
495 * @param generation the current generation
496 * @param totalGenerations the overall number of generations
497 * @param durations the timing (meta) information
498 * @param killCount the number of individuals which has been killed
499 * @param invalidCount the number of individuals which has been removed as
500 * invalid
501 * @param alterCount the number of individuals which has been altered
502 * @param <G> the gene type
503 * @param <C> the fitness type
504 * @return an new evolution result object
505 * @throws java.lang.NullPointerException if one of the parameters is
506 * {@code null}
507 */
508 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
509 EvolutionResult<G, C> of(
510 final Optimize optimize,
511 final Population<G, C> population,
512 final long generation,
513 final long totalGenerations,
514 final EvolutionDurations durations,
515 final int killCount,
516 final int invalidCount,
517 final int alterCount
518 ) {
519 return new EvolutionResult<>(
520 optimize,
521 population,
522 generation,
523 totalGenerations,
524 durations,
525 killCount,
526 invalidCount,
527 alterCount
528 );
529 }
530
531 /**
532 * Return an new {@code EvolutionResult} object with the given values.
533 *
534 * @param optimize the optimization strategy used
535 * @param population the population after the evolution step
536 * @param generation the current generation
537 * @param durations the timing (meta) information
538 * @param killCount the number of individuals which has been killed
539 * @param invalidCount the number of individuals which has been removed as
540 * invalid
541 * @param alterCount the number of individuals which has been altered
542 * @param <G> the gene type
543 * @param <C> the fitness type
544 * @return an new evolution result object
545 * @throws java.lang.NullPointerException if one of the parameters is
546 * {@code null}
547 */
548 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
549 EvolutionResult<G, C> of(
550 final Optimize optimize,
551 final Population<G, C> population,
552 final long generation,
553 final EvolutionDurations durations,
554 final int killCount,
555 final int invalidCount,
556 final int alterCount
557 ) {
558 return new EvolutionResult<>(
559 optimize,
560 population,
561 generation,
562 generation,
563 durations,
564 killCount,
565 invalidCount,
566 alterCount
567 );
568 }
569
570 }
|