001 /*
002 * Java Genetic Algorithm Library (jenetics-1.5.0).
003 * Copyright (c) 2007-2013 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
019 */
020 package org.jenetics;
021
022 import static java.lang.Double.NaN;
023 import static java.lang.String.format;
024 import static java.util.Objects.requireNonNull;
025 import static org.jenetics.util.object.eq;
026 import static org.jenetics.util.object.hashCodeOf;
027
028 import java.text.NumberFormat;
029 import java.text.ParseException;
030 import java.util.Locale;
031
032 import javax.measure.Measurable;
033 import javax.measure.Measure;
034 import javax.measure.MeasureFormat;
035 import javax.measure.quantity.Duration;
036 import javax.measure.unit.SI;
037 import javax.measure.unit.UnitFormat;
038
039 import javolution.lang.Immutable;
040 import javolution.xml.XMLFormat;
041 import javolution.xml.XMLSerializable;
042 import javolution.xml.stream.XMLStreamException;
043
044 import org.jscience.mathematics.number.Float64;
045 import org.jscience.mathematics.number.Integer64;
046
047 import org.jenetics.stat.Variance;
048 import org.jenetics.util.FinalReference;
049 import org.jenetics.util.accumulators;
050 import org.jenetics.util.accumulators.MinMax;
051
052 /**
053 * Data object which holds performance indicators of a given {@link Population}.
054 *
055 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
056 * @since 1.0
057 * @version 1.0 — <em>$Date: 2013-12-05 $</em>
058 */
059 public class Statistics<G extends Gene<?, G>, C extends Comparable<? super C>>
060 implements
061 Immutable,
062 XMLSerializable
063 {
064
065 /**
066 * Builder for the Statistics class.
067 *
068 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
069 * @since 1.0
070 * @version 1.0 — <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 — <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 — <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
|