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