001 /*
002 * Java Genetic Algorithm Library (jenetics-1.6.0).
003 * Copyright (c) 2007-2014 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;
021
022 import static java.lang.Double.NaN;
023 import static java.lang.String.format;
024 import static java.util.Objects.requireNonNull;
025 import static org.jenetics.internal.util.object.eq;
026
027 import java.text.NumberFormat;
028 import java.text.ParseException;
029 import java.util.Locale;
030
031 import javax.measure.Measurable;
032 import javax.measure.Measure;
033 import javax.measure.MeasureFormat;
034 import javax.measure.quantity.Duration;
035 import javax.measure.unit.SI;
036 import javax.measure.unit.UnitFormat;
037
038 import javolution.lang.Immutable;
039 import javolution.xml.XMLFormat;
040 import javolution.xml.XMLSerializable;
041 import javolution.xml.stream.XMLStreamException;
042
043 import org.jscience.mathematics.number.Float64;
044 import org.jscience.mathematics.number.Integer64;
045
046 import org.jenetics.internal.util.HashBuilder;
047
048 import org.jenetics.stat.Variance;
049 import org.jenetics.util.FinalReference;
050 import org.jenetics.util.accumulators;
051 import org.jenetics.util.accumulators.MinMax;
052
053 /**
054 * Data object which holds performance indicators of a given {@link Population}.
055 *
056 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
057 * @since 1.0
058 * @version 1.6 — <em>$Date: 2014-03-01 $</em>
059 */
060 public class Statistics<G extends Gene<?, G>, C extends Comparable<? super C>>
061 implements
062 Immutable,
063 XMLSerializable
064 {
065
066 /**
067 * Builder for the Statistics class.
068 *
069 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
070 * @since 1.0
071 * @version 1.0 — <em>$Date: 2014-03-01 $</em>
072 */
073 public static class Builder<
074 G extends Gene<?, G>,
075 C extends Comparable<? super C>
076 >
077 {
078 protected Optimize _optimize = Optimize.MAXIMUM;
079 protected int _generation = 0;
080 protected Phenotype<G, C> _best = null;
081 protected Phenotype<G, C> _worst = null;
082 protected int _samples = 0;
083 protected double _ageMean = NaN;
084 protected double _ageVariance = NaN;
085 protected int _killed = 0;
086 protected int _invalid = 0;
087
088 /**
089 * Create a new Statistics builder.
090 */
091 public Builder() {
092 }
093
094 /**
095 * Set the values of this builder with the values of the given
096 * {@code statistics}.
097 *
098 * @param statistics the statistics values. If the {@code statistics}
099 * is {@code null} nothing is set.
100 * @return this builder.
101 */
102 public Builder<G, C> statistics(final Statistics<G, C> statistics) {
103 if (statistics != null) {
104 _optimize = statistics._optimize;
105 _generation = statistics._generation;
106 _best = statistics._best;
107 _worst = statistics._worst;
108 _samples = statistics._samples;
109 _ageMean = statistics._ageMean;
110 _ageVariance = statistics._ageVariance;
111 _killed = statistics._killed;
112 _invalid = statistics._invalid;
113 }
114 return this;
115 }
116
117 public Builder<G, C> optimize(final Optimize optimize) {
118 _optimize = requireNonNull(optimize, "Optimize strategy");
119 return this;
120 }
121
122 /**
123 * @see Statistics#getGeneration()
124 */
125 public Builder<G, C> generation(final int generation) {
126 _generation = generation;
127 return this;
128 }
129
130 /**
131 * @see Statistics#getBestPhenotype()
132 */
133 public Builder<G, C> bestPhenotype(final Phenotype<G, C> best) {
134 _best = best;
135 return this;
136 }
137
138 /**
139 * @see Statistics#getWorstPhenotype()
140 */
141 public Builder<G, C> worstPhenotype(final Phenotype<G, C> worst) {
142 _worst = worst;
143 return this;
144 }
145
146 /**
147 * @see Statistics#getSamples()
148 */
149 public Builder<G, C> samples(final int samples) {
150 _samples = samples;
151 return this;
152 }
153
154 /**
155 * @see Statistics#getAgeMean()
156 */
157 public Builder<G, C> ageMean(final double ageMean) {
158 _ageMean = ageMean;
159 return this;
160 }
161
162 /**
163 * @see Statistics#getAgeVariance()
164 */
165 public Builder<G, C> ageVariance(final double ageVariance) {
166 _ageVariance = ageVariance;
167 return this;
168 }
169
170 /**
171 * @see Statistics#getInvalid()
172 */
173 public Builder<G, C> invalid(final int invalid) {
174 _invalid = invalid;
175 return this;
176 }
177
178 /**
179 * @see Statistics#getKilled()
180 */
181 public Builder<G, C> killed(final int killed) {
182 _killed = killed;
183 return this;
184 }
185
186 /**
187 * Return a new Statistics object with the builder values.
188 *
189 * @return new Statistics object with the builder values.
190 */
191 public Statistics<G, C> build() {
192 return new Statistics<>(
193 _optimize,
194 _generation,
195 _best,
196 _worst,
197 _samples,
198 _ageMean,
199 _ageVariance,
200 _killed,
201 _invalid
202 );
203 }
204 }
205
206 private static final long serialVersionUID = 2L;
207
208 protected final Optimize _optimize;
209 protected final int _generation;
210 protected final Phenotype<G, C> _best;
211 protected final Phenotype<G, C> _worst;
212 protected final int _samples;
213 protected final double _ageMean;
214 protected final double _ageVariance;
215 protected final int _killed;
216 protected final int _invalid;
217
218 private final FinalReference<Time> _time = new FinalReference<>(new Time());
219
220
221 /**
222 * Evaluates statistic values from a given population. The given phenotypes
223 * may be {@code null}
224 */
225 protected Statistics(
226 final Optimize optimize,
227 final int generation,
228 final Phenotype<G, C> best,
229 final Phenotype<G, C> worst,
230 final int samples,
231 final double ageMean,
232 final double ageVariance,
233 final int killed,
234 final int invalid
235 ) {
236 _optimize = optimize;
237 _generation = generation;
238 _best = best;
239 _worst = worst;
240 _samples = samples;
241 _ageMean = ageMean;
242 _ageVariance = ageVariance;
243 _killed = killed;
244 _invalid = invalid;
245 }
246
247 /**
248 * Return the optimize strategy of the GA.
249 *
250 * @return the optimize strategy of the GA.
251 */
252 public Optimize getOptimize() {
253 return _optimize;
254 }
255
256 /**
257 * Return the generation of this statistics.
258 *
259 * @return the generation of this statistics.
260 */
261 public int getGeneration() {
262 return _generation;
263 }
264
265 /**
266 * Return the time statistic object which contains the durations of the
267 * different GA execution steps.
268 *
269 * @return the time statistic object.
270 */
271 public Time getTime() {
272 return _time.get();
273 }
274
275 /**
276 * Return the best population Phenotype.
277 *
278 * @return The best population Phenotype.
279 */
280 public Phenotype<G, C> getBestPhenotype() {
281 return _best;
282 }
283
284 /**
285 * Return the worst population Phenotype.
286 *
287 * @return The worst population Phenotype.
288 */
289 public Phenotype<G, C> getWorstPhenotype() {
290 return _worst;
291 }
292
293 /**
294 * Return the best population fitness.
295 *
296 * @return The best population fitness.
297 */
298 public C getBestFitness() {
299 return _best != null ? _best.getFitness() : null;
300 }
301
302 /**
303 * Return the worst population fitness.
304 *
305 * @return The worst population fitness.
306 */
307 public C getWorstFitness() {
308 return _worst != null ? _worst.getFitness() : null;
309 }
310
311 /**
312 * Return the number of samples this statistics has aggregated.
313 *
314 * @return the number of samples this statistics has aggregated.
315 */
316 public int getSamples() {
317 return _samples;
318 }
319
320 /**
321 * Return the average (mean) age of the individuals of the aggregated
322 * population.
323 *
324 * @return the average population age.
325 */
326 public double getAgeMean() {
327 return _ageMean;
328 }
329
330 /**
331 * Return the age variance of the individuals of the aggregated population.
332 *
333 * @return the age variance of the individuals of the aggregated population.
334 */
335 public double getAgeVariance() {
336 return _ageVariance;
337 }
338
339 /**
340 * Return the number of invalid individuals.
341 *
342 * @return the number of invalid individuals.
343 */
344 public int getInvalid() {
345 return _invalid;
346 }
347
348 /**
349 * Return the number of killed individuals.
350 *
351 * @return the number of killed individuals.
352 */
353 public int getKilled() {
354 return _killed;
355 }
356
357 @Override
358 public int hashCode() {
359 return HashBuilder.of(getClass()).
360 and(_optimize).
361 and(_generation).
362 and(_ageMean).
363 and(_ageVariance).
364 and(_best).
365 and(_worst).
366 and(_invalid).
367 and(_samples).
368 and(_killed).value();
369 }
370
371 @Override
372 public boolean equals(final Object obj) {
373 if (obj == this) {
374 return true;
375 }
376 if (obj == null || getClass() != obj.getClass()) {
377 return false;
378 }
379
380 final Statistics<?, ?> statistics = (Statistics<?, ?>)obj;
381 return eq(_optimize, statistics._optimize) &&
382 eq(_generation, statistics._generation) &&
383 eq(_ageMean, statistics._ageMean) &&
384 eq(_ageVariance, statistics._ageVariance) &&
385 eq(_best, statistics._best) &&
386 eq(_worst, statistics._worst) &&
387 eq(_invalid, statistics._invalid) &&
388 eq(_samples, statistics._samples) &&
389 eq(_killed, statistics._killed);
390 }
391
392 @Override
393 public String toString() {
394 final String spattern = "| %28s: %-26s|\n";
395 final String fpattern = "| %28s: %-26.11f|\n";
396 final String ipattern = "| %28s: %-26d|\n";
397
398 final StringBuilder out = new StringBuilder();
399 out.append("+---------------------------------------------------------+\n");
400 out.append("| Population Statistics |\n");
401 out.append("+---------------------------------------------------------+\n");
402 out.append(format(fpattern, "Age mean", _ageMean));
403 out.append(format(fpattern, "Age variance", _ageVariance));
404 out.append(format(ipattern, "Samples", _samples));
405 out.append(format(spattern, "Best fitness", getBestFitness()));
406 out.append(format(spattern, "Worst fitness", getWorstFitness()));
407 out.append("+---------------------------------------------------------+");
408
409 return out.toString();
410 }
411
412 @SuppressWarnings({ "unchecked", "rawtypes" })
413 static final XMLFormat<Statistics> XML =
414 new XMLFormat<Statistics>(Statistics.class)
415 {
416 private static final String OPTIMIZE = "optimize";
417 private static final String GENERATION = "generation";
418 private static final String SAMPLES = "samples";
419 private static final String AGE_MEAN = "age-mean";
420 private static final String AGE_VARIANCE = "age-variance";
421 private static final String BEST_PHENOTYPE = "best-phenotype";
422 private static final String WORST_PHENOTYPE = "worst-phenotype";
423 private static final String STATISTICS_TIME = "statistics-time";
424 private static final String INVALID = "invalid";
425 private static final String KILLED = "killed";
426
427 @Override
428 public Statistics newInstance(final Class<Statistics> cls, final InputElement xml)
429 throws XMLStreamException
430 {
431 final Optimize optimize = Optimize.valueOf(
432 xml.getAttribute(OPTIMIZE, Optimize.MAXIMUM.name())
433 );
434 final int generation = xml.getAttribute(GENERATION, 0);
435 final int samples = xml.getAttribute(SAMPLES, 1);
436 final Float64 meanAge = xml.get(AGE_MEAN);
437 final Float64 varianceAge = xml.get(AGE_VARIANCE);
438 final Integer64 invalid = xml.get(INVALID);
439 final Integer64 killed = xml.get(KILLED);
440 final Phenotype best = xml.get(BEST_PHENOTYPE);
441 final Phenotype worst = xml.get(WORST_PHENOTYPE);
442
443 final Statistics statistics = new Statistics(
444 optimize,
445 generation,
446 best,
447 worst,
448 samples,
449 meanAge.doubleValue(),
450 varianceAge.doubleValue(),
451 killed.intValue(),
452 invalid.intValue()
453 );
454 statistics._time.set(xml.get(STATISTICS_TIME));
455
456 return statistics;
457
458 }
459 @Override
460 public void write(final Statistics s, final OutputElement xml)
461 throws XMLStreamException
462 {
463 xml.setAttribute(OPTIMIZE, s._optimize.name());
464 xml.setAttribute(GENERATION, s._generation);
465 xml.setAttribute(SAMPLES, s._samples);
466 xml.add(Float64.valueOf(s._ageMean), AGE_MEAN);
467 xml.add(Float64.valueOf(s._ageVariance), AGE_VARIANCE);
468 xml.add(Integer64.valueOf(s._invalid), INVALID);
469 xml.add(Integer64.valueOf(s._killed), KILLED);
470 xml.add(s._best, BEST_PHENOTYPE);
471 xml.add(s._worst, WORST_PHENOTYPE);
472 xml.add(s._time.get(), STATISTICS_TIME);
473 }
474 @Override
475 public void read(final InputElement xml, final Statistics p) {
476 }
477 };
478
479
480 /**
481 * Class which holds time statistic values.
482 *
483 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
484 * @since 1.0
485 * @version 1.6 — <em>$Date: 2014-03-01 $</em>
486 */
487 public static final class Time implements XMLSerializable {
488 private static final long serialVersionUID = 1L;
489
490 private static final Measurable<Duration> ZERO = Measure.valueOf(
491 0, SI.MILLI(SI.SECOND)
492 );
493
494 /**
495 * Create a new time object with zero time values. The time references
496 * can only be set once. If you try to set the values twice an
497 * {@link IllegalStateException} is thrown.
498 */
499 public Time() {
500 }
501
502 /**
503 * The overall execution time.
504 * The time can be set only once, otherwise an IllegalArgumentException
505 * is thrown.
506 */
507 public final FinalReference<Measurable<Duration>>
508 execution = new FinalReference<>(ZERO);
509
510 /**
511 * The selection time.
512 * The time can be set only once, otherwise an IllegalArgumentException
513 * is thrown.
514 */
515 public final FinalReference<Measurable<Duration>>
516 selection = new FinalReference<>(ZERO);
517
518 /**
519 * The alter time.
520 * The time can be set only once, otherwise an IllegalArgumentException
521 * is thrown.
522 */
523 public final FinalReference<Measurable<Duration>>
524 alter = new FinalReference<>(ZERO);
525
526 /**
527 * Combination time between offspring and survivors.
528 * The time can be set only once, otherwise an IllegalArgumentException
529 * is thrown.
530 */
531 public final FinalReference<Measurable<Duration>>
532 combine = new FinalReference<>(ZERO);
533
534 /**
535 * The evaluation time.
536 * The time can be set only once, otherwise an IllegalArgumentException
537 * is thrown.
538 */
539 public final FinalReference<Measurable<Duration>>
540 evaluation = new FinalReference<>(ZERO);
541
542 /**
543 * The statistics time.
544 * The time can be set only once, otherwise an IllegalArgumentException
545 * is thrown.
546 */
547 public final FinalReference<Measurable<Duration>>
548 statistics = new FinalReference<>(ZERO);
549
550
551 @Override
552 public int hashCode() {
553 return HashBuilder.of(getClass()).
554 and(alter).
555 and(combine).
556 and(evaluation).
557 and(execution).
558 and(selection).
559 and(statistics).value();
560 }
561
562 @Override
563 public boolean equals(final Object object) {
564 if (object == this) {
565 return true;
566 }
567 if (object == null || object.getClass() != getClass()) {
568 return false;
569 }
570
571 final Statistics.Time time = (Statistics.Time)object;
572 return eq(alter.get(), time.alter.get()) &&
573 eq(combine.get(), time.combine.get()) &&
574 eq(evaluation.get(), time.evaluation.get()) &&
575 eq(execution.get(), time.execution.get()) &&
576 eq(selection.get(), time.selection.get()) &&
577 eq(statistics.get(), time.statistics.get());
578 }
579
580 @Override
581 public String toString() {
582 final String pattern = "| %28s: %-26.11f|\n";
583
584 final StringBuilder out = new StringBuilder();
585 out.append("+---------------------------------------------------------+\n");
586 out.append("| Time Statistics |\n");
587 out.append("+---------------------------------------------------------+\n");
588 out.append(format(pattern, "Select time", selection.get().doubleValue(SI.SECOND)));
589 out.append(format(pattern, "Alter time", alter.get().doubleValue(SI.SECOND)));
590 out.append(format(pattern, "Combine time", combine.get().doubleValue(SI.SECOND)));
591 out.append(format(pattern, "Fitness calculation time", evaluation.get().doubleValue(SI.SECOND)));
592 out.append(format(pattern, "Statistics calculation time", statistics.get().doubleValue(SI.SECOND)));
593 out.append(format(pattern, "Overall execution time", execution.get().doubleValue(SI.SECOND)));
594 out.append("+---------------------------------------------------------+");
595
596 return out.toString();
597 }
598
599 /* ********************************************************************
600 * XML object serialization
601 * ********************************************************************/
602
603 static final XMLFormat<Statistics.Time> XML =
604 new XMLFormat<Statistics.Time>(Statistics.Time.class)
605 {
606 private static final String ALTER_TIME = "alter-time";
607 private static final String COMBINE_TIME = "combine-time";
608 private static final String EVALUATION_TIME = "evaluation-time";
609 private static final String EXECUTION_TIME = "execution-time";
610 private static final String SELECTION_TIME = "selection-time";
611 private static final String STATISTICS_TIME = "statistics-time";
612
613 @SuppressWarnings("unchecked")
614 @Override
615 public Statistics.Time newInstance(
616 final Class<Statistics.Time> cls, final InputElement xml
617 )
618 throws XMLStreamException
619 {
620 final MeasureFormat format = getMeasureFormat();
621 final Statistics.Time time = new Statistics.Time();
622
623 try {
624 time.alter.set((Measurable<Duration>)format.parseObject(
625 (String)xml.get(ALTER_TIME)
626 ));
627 time.combine.set((Measurable<Duration>)format.parseObject(
628 (String)xml.get(COMBINE_TIME)
629 ));
630 time.evaluation.set((Measurable<Duration>)format.parseObject(
631 (String)xml.get(EVALUATION_TIME)
632 ));
633 time.execution.set((Measurable<Duration>)format.parseObject(
634 (String)xml.get(EXECUTION_TIME)
635 ));
636 time.selection.set((Measurable<Duration>)format.parseObject(
637 (String)xml.get(SELECTION_TIME)
638 ));
639 time.statistics.set((Measurable<Duration>)format.parseObject(
640 (String)xml.get(STATISTICS_TIME)
641 ));
642 } catch (ParseException e) {
643 throw new XMLStreamException(e);
644 }
645 return time;
646
647 }
648 @Override
649 public void write(final Statistics.Time s, final OutputElement xml)
650 throws XMLStreamException
651 {
652 xml.add(fd(s.alter.get()), ALTER_TIME);
653 xml.add(fd(s.combine.get()), COMBINE_TIME);
654 xml.add(fd(s.evaluation.get()), EVALUATION_TIME);
655 xml.add(fd(s.execution.get()), EXECUTION_TIME);
656 xml.add(fd(s.selection.get()), SELECTION_TIME);
657 xml.add(fd(s.statistics.get()), STATISTICS_TIME);
658 }
659 @Override
660 public void read(final InputElement xml, final Statistics.Time p) {
661 }
662
663 private MeasureFormat getMeasureFormat() {
664 final NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
665 nf.setMinimumFractionDigits(25);
666 final UnitFormat uf = UnitFormat.getInstance(Locale.ENGLISH);
667
668 return MeasureFormat.getInstance(nf, uf);
669 }
670 };
671
672 private static String fd(final Measurable<Duration> duration) {
673 return String.format("%d ns", duration.longValue(SI.NANO(SI.SECOND)));
674 }
675
676 }
677
678
679 /**
680 * Class for calculating the statistics. This class serves also as factory
681 * for the Statistics class.
682 *
683 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
684 * @since 1.0
685 * @version 1.0 — <em>$Date: 2014-03-01 $</em>
686 */
687 public static class Calculator<
688 G extends Gene<?, G>,
689 C extends Comparable<? super C>
690 >
691 {
692
693 /**
694 * Create a new calculator object.
695 */
696 public Calculator() {
697 }
698
699 /**
700 * Create a new statistics object from the given {@code population} at
701 * the given {@code generation}.
702 *
703 * @param population the population to aggregate.
704 * @param generation the current GA generation.
705 * @param opt the optimization <i>direction</i>.
706 * @return a new statistics object generated from the given arguments.
707 */
708 public Statistics.Builder<G, C> evaluate(
709 final Iterable<? extends Phenotype<G, C>> population,
710 final int generation,
711 final Optimize opt
712 ) {
713 final Builder<G, C> builder = new Builder<>();
714 builder.generation(generation);
715 builder.optimize(opt);
716
717 final MinMax<Phenotype<G, C>> minMax = new MinMax<>();
718 final Variance<Integer> age = new Variance<>();
719
720 accumulators.<Phenotype<G, C>>accumulate(
721 population,
722 minMax,
723 age.map(Phenotype.Age(generation))
724 );
725
726 builder.bestPhenotype(opt.best(minMax.getMax(), minMax.getMin()));
727 builder.worstPhenotype(opt.worst(minMax.getMax(), minMax.getMin()));
728 builder.samples((int)minMax.getSamples());
729 builder.ageMean(age.getMean());
730 builder.ageVariance(age.getVariance());
731
732 return builder;
733 }
734
735 }
736
737 }
|