NumberStatistics.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 org.jenetics.internal.util.object.eq;

import javolution.xml.XMLFormat;
import javolution.xml.stream.XMLStreamException;

import org.jscience.mathematics.number.Float64;

import org.jenetics.internal.util.HashBuilder;

import org.jenetics.stat.Variance;
import org.jenetics.util.accumulators;
import org.jenetics.util.accumulators.MinMax;

/**
 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
 * @since 1.0
 * @version 1.0 &mdash; <em>$Date: 2014-03-01 $</em>
 */
public class NumberStatistics<
	G extends Gene<?, G>,
	R extends Number & Comparable<? super R>
>
	extends Statistics<G, R>
{

	/**
	 * Builder for the NumberStatistics class.
	 *
	 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
	 * @since 1.0
	 * @version 1.0 &mdash; <em>$Date: 2014-03-01 $</em>
	 */
	public static class Builder<
		G extends Gene<?, G>,
		R extends Number & Comparable<? super R>
	>
		extends Statistics.Builder<G, R>
	{
		protected double _fitnessMean = NaN;
		protected double _fitnessVariance = NaN;
		protected double _standardError = NaN;

		/**
		 * Create a new NumberStatistics builder.
		 */
		public Builder() {
		}

		@Override
		public Builder<G, R> statistics(final Statistics<G, R> statistics) {
			super.statistics(statistics);
			return this;
		}

		/**
		 * 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.
		 */
		public Builder<G, R> statistics(final NumberStatistics<G, R> statistics) {
			if (statistics != null) {
				super.statistics(statistics);
				_fitnessMean = statistics._fitnessMean;
				_fitnessVariance = statistics._fitnessVariance;
				_standardError = statistics._standardError;
			}
			return this;
		}

		/**
		 * @see NumberStatistics#getFitnessMean()
		 */
		public Builder<G, R> fitnessMean(final double fitnessMean) {
			_fitnessMean = fitnessMean;
			return this;
		}

		/**
		 * @see NumberStatistics#getFitnessVariance()
		 */
		public Builder<G, R> fitnessVariance(final double fitnessVariance) {
			_fitnessVariance = fitnessVariance;
			return this;
		}

		/**
		 * @see NumberStatistics#getStandardError()
		 */
		public Builder<G, R> standardError(final double standardError) {
			_standardError = standardError;
			return this;
		}

		@Override
		public NumberStatistics<G, R> build() {
			return new NumberStatistics<>(
				_optimize,
				_generation,
				_best,
				_worst,
				_fitnessMean,
				_fitnessVariance,
				_samples,
				_ageMean,
				_ageVariance,
				_standardError,
				_killed,
				_invalid
			);
		}
	}

	private static final long serialVersionUID = 2L;

	protected final double _fitnessMean;
	protected final double _fitnessVariance;
	protected final double _standardError;

	protected NumberStatistics(
		final Optimize optimize,
		final int generation,
		final Phenotype<G, R> best,
		final Phenotype<G, R> worst,
		final double fitnessMean,
		final double fitnessVariance,
		final int samples,
		final double ageMean,
		final double ageVariance,
		final double errorOfMean,
		final int killed,
		final int invalid
	) {
		super(
			optimize,
			generation,
			best,
			worst,
			samples,
			ageMean,
			ageVariance,
			killed,
			invalid
		);

		_fitnessMean = fitnessMean;
		_fitnessVariance = fitnessVariance;
		_standardError = errorOfMean;
	}

	/**
	 * Return the mean of the fitness values.
	 *
	 * @return the mean of the fitness values.
	 */
	public double getFitnessMean() {
		return _fitnessMean;
	}

	/**
	 * Return the variance of the fitness values.
	 *
	 * @return the variance of the fitness values.
	 */
	public double getFitnessVariance() {
		return _fitnessVariance;
	}

	/**
	 * Return the <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Standard_error_%28statistics%29">
	 * Standard error
	 * </a> of the calculated fitness mean.
	 *
	 * @return the standard error of the calculated fitness mean.
	 */
	public double getStandardError() {
		return _standardError;
	}

	@Override
	public int hashCode() {
		return HashBuilder.of(getClass()).
				and(super.hashCode()).
				and(_fitnessMean).
				and(_fitnessVariance).
				and(_standardError).value();
	}

	@Override
	public boolean equals(final Object obj) {
		if (obj == this) {
			return true;
		}
		if (!(obj instanceof NumberStatistics<?, ?>)) {
			return false;
		}

		final NumberStatistics<?, ?> statistics = (NumberStatistics<?, ?>) obj;
		return eq(statistics._fitnessMean, _fitnessMean) &&
				eq(statistics._fitnessVariance, _fitnessVariance) &&
				eq(statistics._standardError, _standardError) &&
				super.equals(obj);
	}

	@Override
	public String toString() {
		final String fpattern = "| %28s: %-26.11f|\n";

		final StringBuilder out = new StringBuilder();
		out.append(super.toString()).append("\n");
		out.append("+---------------------------------------------------------+\n");
		out.append("|  Fitness Statistics                                     |\n");
		out.append("+---------------------------------------------------------+\n");
		out.append(format(fpattern, "Fitness mean", _fitnessMean));
		out.append(format(fpattern, "Fitness variance", _fitnessVariance));
		out.append(format(fpattern, "Fitness error of mean", _standardError));
		out.append("+---------------------------------------------------------+");

		return out.toString();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	static final XMLFormat<NumberStatistics> XML =
		new XMLFormat<NumberStatistics>(NumberStatistics.class)
	{
		private static final String FITNESS_MEAN = "fitness-mean";
		private static final String FITNESS_VARIANCE = "fitness-variance";
		private static final String ERROR_OF_MEAN = "error-of-mean";

		@Override
		public NumberStatistics newInstance(
			final Class<NumberStatistics> type,
			final InputElement xml
		)
			throws XMLStreamException
		{
			final Statistics stats = Statistics.XML.newInstance(
					Statistics.class, xml
				);
			final Float64 fitnessMean = xml.get(FITNESS_MEAN);
			final Float64 fitnessVariance = xml.get(FITNESS_VARIANCE);
			final Float64 errorOfMean = xml.get(ERROR_OF_MEAN);

			final Builder builder = new Builder().statistics(stats);
			builder.fitnessMean(fitnessMean.doubleValue());
			builder.fitnessVariance(fitnessVariance.doubleValue());
			builder.standardError(errorOfMean.doubleValue());

			return builder.build();
		}

		@Override
		public void write(final NumberStatistics s, final OutputElement xml)
			throws XMLStreamException
		{
			Statistics.XML.write(s, xml);
			xml.add(Float64.valueOf(s.getFitnessMean()), FITNESS_MEAN);
			xml.add(Float64.valueOf(s.getFitnessVariance()), FITNESS_VARIANCE);
			xml.add(Float64.valueOf(s.getStandardError()), ERROR_OF_MEAN);
		}

		@Override
		public void read(final InputElement xml, final NumberStatistics p) {
		}
	};

	/**
	 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
	 * @since 1.0
	 * @version 1.0 &mdash; <em>$Date: 2014-03-01 $</em>
	 */
	public static class Calculator<
		G extends Gene<?, G>,
		R extends Number & Comparable<? super R>
	>
		extends Statistics.Calculator<G, R>
	{

		public Calculator() {
		}

		@Override
		public NumberStatistics.Builder<G, R> evaluate(
			final Iterable<? extends Phenotype<G, R>> population,
			final int generation,
			final Optimize opt
		) {
			final Builder<G, R> builder = new Builder<>();
			builder.generation(generation);
			builder.optimize(opt);

			final MinMax<Phenotype<G, R>> minMax = new MinMax<>();
			final Variance<Integer> age = new Variance<>();
			final Variance<R> fitness = new Variance<>();

			accumulators.<Phenotype<G, R>>accumulate(
					population,
					minMax,
					age.map(Phenotype.Age(generation)),
					fitness.map(Phenotype.<R>Fitness())
				);
			builder.bestPhenotype(opt.best(minMax.getMax(), minMax.getMin()));
			builder.worstPhenotype(opt.worst(minMax.getMax(), minMax.getMin()));
			builder.fitnessMean(fitness.getMean());
			builder.fitnessVariance(fitness.getVariance());
			builder.samples((int)minMax.getSamples());
			builder.ageMean(age.getMean());
			builder.ageVariance(age.getVariance());
			builder.standardError(fitness.getStandardError());

			return builder;
		}
	}

}