Statistics.java
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 &mdash; <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 &mdash; <em>$Date: 2014-03-01 $</em>
072      */
073     public static class Builder<
074         extends Gene<?, G>,
075         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 &mdash; <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 &mdash; <em>$Date: 2014-03-01 $</em>
686      */
687     public static class Calculator<
688         extends Gene<?, G>,
689         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 }