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 */
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.util.object.eq;
026import static org.jenetics.util.object.hashCodeOf;
027
028import java.text.NumberFormat;
029import java.text.ParseException;
030import java.util.Locale;
031
032import javax.measure.Measurable;
033import javax.measure.Measure;
034import javax.measure.MeasureFormat;
035import javax.measure.quantity.Duration;
036import javax.measure.unit.SI;
037import javax.measure.unit.UnitFormat;
038
039import javolution.lang.Immutable;
040import javolution.xml.XMLFormat;
041import javolution.xml.XMLSerializable;
042import javolution.xml.stream.XMLStreamException;
043
044import org.jscience.mathematics.number.Float64;
045import org.jscience.mathematics.number.Integer64;
046
047import org.jenetics.stat.Variance;
048import org.jenetics.util.FinalReference;
049import org.jenetics.util.accumulators;
050import 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 */
059public 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                G extends Gene<?, G>,
074                C 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                G extends Gene<?, G>,
681                C 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
732