Statistics.java
/*
* Java Genetic Algorithm Library (@__identifier__@).
* Copyright (c) @__year__@ Franz Wilhelmstötter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author:
* Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
*/
package org.jenetics;
import static java.lang.Double.NaN;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static org.jenetics.internal.util.object.eq;
import java.io.Serializable;
import java.util.concurrent.Executor;
import org.jenetics.internal.util.HashBuilder;
import org.jenetics.stat.Variance;
import org.jenetics.util.Duration;
import org.jenetics.util.FinalReference;
import org.jenetics.util.accumulators;
import org.jenetics.util.accumulators.MinMax;
/**
* Data object which holds performance indicators of a given {@link Population}.
*
* @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
* @since 1.0
* @version 2.0 — <em>$Date: 2014-04-05 $</em>
*/
public class Statistics<G extends Gene<?, G>, C extends Comparable<? super C>>
implements Serializable
{
/**
* Builder for the Statistics class.
*
* @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
* @since 1.0
* @version 2.0 — <em>$Date: 2014-04-05 $</em>
*/
public static class Builder<
G extends Gene<?, G>,
C extends Comparable<? super C>
>
{
protected Optimize _optimize = Optimize.MAXIMUM;
protected int _generation = 0;
protected Phenotype<G, C> _best = null;
protected Phenotype<G, C> _worst = null;
protected int _samples = 0;
protected double _ageMean = NaN;
protected double _ageVariance = NaN;
protected int _killed = 0;
protected int _invalid = 0;
/**
* Create a new Statistics builder.
*/
public Builder() {
}
/**
* Set the values of this builder with the values of the given
* {@code statistics}.
*
* @param statistics the statistics values. If the {@code statistics}
* is {@code null} nothing is set.
* @return this builder instance.
*/
public Builder<G, C> statistics(final Statistics<G, C> statistics) {
if (statistics != null) {
_optimize = statistics._optimize;
_generation = statistics._generation;
_best = statistics._best;
_worst = statistics._worst;
_samples = statistics._samples;
_ageMean = statistics._ageMean;
_ageVariance = statistics._ageVariance;
_killed = statistics._killed;
_invalid = statistics._invalid;
}
return this;
}
public Builder<G, C> optimize(final Optimize optimize) {
_optimize = requireNonNull(optimize, "Optimize strategy");
return this;
}
/**
* @see Statistics#getGeneration()
*
* @param generation the current GA generation
* @return this builder instance
*/
public Builder<G, C> generation(final int generation) {
_generation = generation;
return this;
}
/**
* @see Statistics#getBestPhenotype()
*
* @param best the best phenotype
* @return this builder instance
*/
public Builder<G, C> bestPhenotype(final Phenotype<G, C> best) {
_best = best;
return this;
}
/**
* @see Statistics#getWorstPhenotype()
*
* @param worst the worst phenotype
* @return this builder instance
*/
public Builder<G, C> worstPhenotype(final Phenotype<G, C> worst) {
_worst = worst;
return this;
}
/**
* @see Statistics#getSamples()
*
* @param samples the number of samples for the statistics object.
* @return this builder instance
*/
public Builder<G, C> samples(final int samples) {
_samples = samples;
return this;
}
/**
* @see Statistics#getAgeMean()
*
* @param ageMean the mean of the population age
* @return this builder instance
*/
public Builder<G, C> ageMean(final double ageMean) {
_ageMean = ageMean;
return this;
}
/**
* @see Statistics#getAgeVariance()
*
* @param ageVariance the variance of the population age
* @return this builder instance
*/
public Builder<G, C> ageVariance(final double ageVariance) {
_ageVariance = ageVariance;
return this;
}
/**
* @see Statistics#getInvalid()
*
* @param invalid the number of valid individuals
* @return this builder instance
*/
public Builder<G, C> invalid(final int invalid) {
_invalid = invalid;
return this;
}
/**
* @see Statistics#getKilled()
*
* @param killed the number of killed individuals
* @return this builder instance
*/
public Builder<G, C> killed(final int killed) {
_killed = killed;
return this;
}
/**
* Return a new Statistics object with the builder values.
*
* @return new Statistics object with the builder values.
*/
public Statistics<G, C> build() {
return new Statistics<>(
_optimize,
_generation,
_best,
_worst,
_samples,
_ageMean,
_ageVariance,
_killed,
_invalid
);
}
}
private static final long serialVersionUID = 3L;
protected final Optimize _optimize;
protected final int _generation;
protected final Phenotype<G, C> _best;
protected final Phenotype<G, C> _worst;
protected final int _samples;
protected final double _ageMean;
protected final double _ageVariance;
protected final int _killed;
protected final int _invalid;
private final FinalReference<Time> _time = new FinalReference<>(new Time());
/**
* Evaluates statistic values from a given population. The given phenotypes
* may be {@code null}
*
* @param optimize the optimization strategy used
* @param generation the generation for this statistics
* @param best best phenotype
* @param worst worst phenotype
* @param samples number of samples of this statistics
* @param ageMean the mean value of the individuals age
* @param ageVariance the variance value of the individuals ages
* @param killed the number of killed individuals
* @param invalid the number of invalid individuals
*/
protected Statistics(
final Optimize optimize,
final int generation,
final Phenotype<G, C> best,
final Phenotype<G, C> worst,
final int samples,
final double ageMean,
final double ageVariance,
final int killed,
final int invalid
) {
_optimize = optimize;
_generation = generation;
_best = best;
_worst = worst;
_samples = samples;
_ageMean = ageMean;
_ageVariance = ageVariance;
_killed = killed;
_invalid = invalid;
}
/**
* Return the optimize strategy of the GA.
*
* @return the optimize strategy of the GA.
*/
public Optimize getOptimize() {
return _optimize;
}
/**
* Return the generation of this statistics.
*
* @return the generation of this statistics.
*/
public int getGeneration() {
return _generation;
}
/**
* Return the time statistic object which contains the durations of the
* different GA execution steps.
*
* @return the time statistic object.
*/
public Time getTime() {
return _time.get();
}
/**
* Return the best population Phenotype.
*
* @return The best population Phenotype.
*/
public Phenotype<G, C> getBestPhenotype() {
return _best;
}
/**
* Return the worst population Phenotype.
*
* @return The worst population Phenotype.
*/
public Phenotype<G, C> getWorstPhenotype() {
return _worst;
}
/**
* Return the best population fitness.
*
* @return The best population fitness.
*/
public C getBestFitness() {
return _best != null ? _best.getFitness() : null;
}
/**
* Return the worst population fitness.
*
* @return The worst population fitness.
*/
public C getWorstFitness() {
return _worst != null ? _worst.getFitness() : null;
}
/**
* Return the number of samples this statistics has aggregated.
*
* @return the number of samples this statistics has aggregated.
*/
public int getSamples() {
return _samples;
}
/**
* Return the average (mean) age of the individuals of the aggregated
* population.
*
* @return the average population age.
*/
public double getAgeMean() {
return _ageMean;
}
/**
* Return the age variance of the individuals of the aggregated population.
*
* @return the age variance of the individuals of the aggregated population.
*/
public double getAgeVariance() {
return _ageVariance;
}
/**
* Return the number of invalid individuals.
*
* @return the number of invalid individuals.
*/
public int getInvalid() {
return _invalid;
}
/**
* Return the number of killed individuals.
*
* @return the number of killed individuals.
*/
public int getKilled() {
return _killed;
}
@Override
public int hashCode() {
return HashBuilder.of(getClass()).
and(_optimize).
and(_generation).
and(_ageMean).
and(_ageVariance).
and(_best).
and(_worst).
and(_invalid).
and(_samples).
and(_killed).value();
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Statistics<?, ?> statistics = (Statistics<?, ?>)obj;
return eq(_optimize, statistics._optimize) &&
eq(_generation, statistics._generation) &&
eq(_ageMean, statistics._ageMean) &&
eq(_ageVariance, statistics._ageVariance) &&
eq(_best, statistics._best) &&
eq(_worst, statistics._worst) &&
eq(_invalid, statistics._invalid) &&
eq(_samples, statistics._samples) &&
eq(_killed, statistics._killed);
}
@Override
public String toString() {
final String spattern = "| %28s: %-26s|\n";
final String fpattern = "| %28s: %-26.11f|\n";
final String ipattern = "| %28s: %-26d|\n";
final StringBuilder out = new StringBuilder();
out.append("+---------------------------------------------------------+\n");
out.append("| Population Statistics |\n");
out.append("+---------------------------------------------------------+\n");
out.append(format(fpattern, "Age mean", _ageMean));
out.append(format(fpattern, "Age variance", _ageVariance));
out.append(format(ipattern, "Samples", _samples));
out.append(format(spattern, "Best fitness", getBestFitness()));
out.append(format(spattern, "Worst fitness", getWorstFitness()));
out.append("+---------------------------------------------------------+");
return out.toString();
}
/**
* Class which holds time statistic values.
*
* @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
* @since 1.0
* @version 2.0 — <em>$Date: 2014-04-05 $</em>
*/
public static final class Time implements Serializable {
private static final long serialVersionUID = 2L;
private static final Duration ZERO = Duration.ofNanos(0);
/**
* Create a new time object with zero time values. The time references
* can only be set once. If you try to set the values twice an
* {@link IllegalStateException} is thrown.
*/
public Time() {
}
/**
* The overall execution time.
* The time can be set only once, otherwise an IllegalArgumentException
* is thrown.
*/
public final FinalReference<Duration>
execution = new FinalReference<>(ZERO);
/**
* The selection time.
* The time can be set only once, otherwise an IllegalArgumentException
* is thrown.
*/
public final FinalReference<Duration>
selection = new FinalReference<>(ZERO);
/**
* The alter time.
* The time can be set only once, otherwise an IllegalArgumentException
* is thrown.
*/
public final FinalReference<Duration>
alter = new FinalReference<>(ZERO);
/**
* Combination time between offspring and survivors.
* The time can be set only once, otherwise an IllegalArgumentException
* is thrown.
*/
public final FinalReference<Duration>
combine = new FinalReference<>(ZERO);
/**
* The evaluation time.
* The time can be set only once, otherwise an IllegalArgumentException
* is thrown.
*/
public final FinalReference<Duration>
evaluation = new FinalReference<>(ZERO);
/**
* The statistics time.
* The time can be set only once, otherwise an IllegalArgumentException
* is thrown.
*/
public final FinalReference<Duration>
statistics = new FinalReference<>(ZERO);
@Override
public int hashCode() {
return HashBuilder.of(getClass()).
and(alter).
and(combine).
and(evaluation).
and(execution).
and(selection).
and(statistics).value();
}
@Override
public boolean equals(final Object object) {
if (object == this) {
return true;
}
if (object == null || object.getClass() != getClass()) {
return false;
}
final Statistics.Time time = (Statistics.Time)object;
return eq(alter.get(), time.alter.get()) &&
eq(combine.get(), time.combine.get()) &&
eq(evaluation.get(), time.evaluation.get()) &&
eq(execution.get(), time.execution.get()) &&
eq(selection.get(), time.selection.get()) &&
eq(statistics.get(), time.statistics.get());
}
@Override
public String toString() {
final String pattern = "| %28s: %-26.11f|\n";
final StringBuilder out = new StringBuilder();
out.append("+---------------------------------------------------------+\n");
out.append("| Time Statistics |\n");
out.append("+---------------------------------------------------------+\n");
out.append(format(pattern, "Select time", selection.get().toSeconds()));
out.append(format(pattern, "Alter time", alter.get().toSeconds()));
out.append(format(pattern, "Combine time", combine.get().toSeconds()));
out.append(format(pattern, "Fitness calculation time", evaluation.get().toSeconds()));
out.append(format(pattern, "Statistics calculation time", statistics.get().toSeconds()));
out.append(format(pattern, "Overall execution time", execution.get().toSeconds()));
out.append("+---------------------------------------------------------+");
return out.toString();
}
}
/**
* Class for calculating the statistics. This class serves also as factory
* for the Statistics class.
*
* @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
* @since 1.0
* @version 2.0 — <em>$Date: 2014-04-05 $</em>
*/
public static class Calculator<
G extends Gene<?, G>,
C extends Comparable<? super C>
>
{
/**
* Create a new calculator object.
*/
public Calculator() {
}
/**
* Create a new statistics object from the given {@code population} at
* the given {@code generation}.
*
* @param population the population to aggregate.
* @param generation the current GA generation.
* @param opt the optimization <i>direction</i>.
* @return a new statistics object generated from the given arguments.
*/
public Statistics.Builder<G, C> evaluate(
final Executor executor,
final Iterable<? extends Phenotype<G, C>> population,
final int generation,
final Optimize opt
) {
final Builder<G, C> builder = new Builder<>();
builder.generation(generation);
builder.optimize(opt);
final MinMax<Phenotype<G, C>> minMax = new MinMax<>();
final Variance<Integer> age = new Variance<>();
accumulators.<Phenotype<G, C>>accumulate(
executor,
population,
minMax,
age.map(Phenotype.Age(generation))
);
builder.bestPhenotype(opt.best(minMax.getMax(), minMax.getMin()));
builder.worstPhenotype(opt.worst(minMax.getMax(), minMax.getMin()));
builder.samples((int)minMax.getSamples());
builder.ageMean(age.getMean());
builder.ageVariance(age.getVariance());
return builder;
}
}
}