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 — <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 — <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 — <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 — <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}