/*
* Copyright (C) 2013 The Guava Authors
*
* 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.
*/
package com.google.common.math;
import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
import com.google.caliper.Param;
import com.google.caliper.api.SkipThisScenarioException;
import com.google.common.primitives.Doubles;
import java.util.Random;
/**
* Benchmarks for various algorithms for computing the mean and/or variance.
*
* @author Louis Wasserman
*/
public class StatsBenchmark {
enum MeanAlgorithm {
SIMPLE {
@Override
double mean(double[] values) {
double sum = 0.0;
for (double value : values) {
sum += value;
}
return sum / values.length;
}
},
KAHAN {
@Override
double mean(double[] values) {
double sum = 0.0;
double c = 0.0;
for (double value : values) {
double y = value - c;
double t = sum + y;
c = (t - sum) - y;
sum = t;
}
return sum / values.length;
}
},
KNUTH {
@Override
double mean(double[] values) {
double mean = values[0];
for (int i = 1; i < values.length; i++) {
mean = mean + (values[i] - mean) / (i + 1);
}
return mean;
}
};
abstract double mean(double[] values);
}
static class MeanAndVariance {
private final double mean;
private final double variance;
MeanAndVariance(double mean, double variance) {
this.mean = mean;
this.variance = variance;
}
@Override
public int hashCode() {
return Doubles.hashCode(mean) * 31 + Doubles.hashCode(variance);
}
}
enum VarianceAlgorithm {
DO_NOT_COMPUTE {
@Override
MeanAndVariance variance(double[] values, MeanAlgorithm meanAlgorithm) {
return new MeanAndVariance(meanAlgorithm.mean(values), 0.0);
}
},
SIMPLE {
@Override
MeanAndVariance variance(double[] values, MeanAlgorithm meanAlgorithm) {
double mean = meanAlgorithm.mean(values);
double sumOfSquaresOfDeltas = 0.0;
for (double value : values) {
double delta = value - mean;
sumOfSquaresOfDeltas += delta * delta;
}
return new MeanAndVariance(mean, sumOfSquaresOfDeltas / values.length);
}
},
KAHAN {
@Override
MeanAndVariance variance(double[] values, MeanAlgorithm meanAlgorithm) {
double mean = meanAlgorithm.mean(values);
double sumOfSquaresOfDeltas = 0.0;
double c = 0.0;
for (double value : values) {
double delta = value - mean;
double deltaSquared = delta * delta;
double y = deltaSquared - c;
double t = sumOfSquaresOfDeltas + deltaSquared;
c = (t - sumOfSquaresOfDeltas) - y;
sumOfSquaresOfDeltas = t;
}
return new MeanAndVariance(mean, sumOfSquaresOfDeltas / values.length);
}
},
KNUTH {
@Override
MeanAndVariance variance(double[] values, MeanAlgorithm meanAlgorithm) {
if (meanAlgorithm != MeanAlgorithm.KNUTH) {
throw new SkipThisScenarioException();
}
double mean = values[0];
double s = 0.0;
for (int i = 1; i < values.length; i++) {
double nextMean = mean + (values[i] - mean) / (i + 1);
s += (values[i] - mean) * (values[i] - nextMean);
mean = nextMean;
}
return new MeanAndVariance(mean, s / values.length);
}
};
abstract MeanAndVariance variance(double[] values, MeanAlgorithm meanAlgorithm);
}
@Param({"100", "10000"})
int n;
@Param
MeanAlgorithm meanAlgorithm;
@Param
VarianceAlgorithm varianceAlgorithm;
private double[][] values = new double[0x100][];
@BeforeExperiment
void setUp() {
Random rng = new Random();
for (int i = 0; i < 0x100; i++) {
values[i] = new double[n];
for (int j = 0; j < n; j++) {
values[i][j] = rng.nextDouble();
}
}
}
@Benchmark int meanAndVariance(int reps) {
int tmp = 0;
for (int i = 0; i < reps; i++) {
tmp += varianceAlgorithm.variance(values[i & 0xFF], meanAlgorithm).hashCode();
}
return tmp;
}
}