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