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 */
020package org.jenetics;
021
022import static java.lang.Double.NaN;
023import static java.lang.String.format;
024import static java.util.Objects.requireNonNull;
025import static org.jenetics.internal.util.object.eq;
026
027import java.text.NumberFormat;
028import java.text.ParseException;
029import java.util.Locale;
030
031import javax.measure.Measurable;
032import javax.measure.Measure;
033import javax.measure.MeasureFormat;
034import javax.measure.quantity.Duration;
035import javax.measure.unit.SI;
036import javax.measure.unit.UnitFormat;
037
038import javolution.lang.Immutable;
039import javolution.xml.XMLFormat;
040import javolution.xml.XMLSerializable;
041import javolution.xml.stream.XMLStreamException;
042
043import org.jscience.mathematics.number.Float64;
044import org.jscience.mathematics.number.Integer64;
045
046import org.jenetics.internal.util.HashBuilder;
047
048import org.jenetics.stat.Variance;
049import org.jenetics.util.FinalReference;
050import org.jenetics.util.accumulators;
051import 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 */
060public 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                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 &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                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}