/*
* Encog(tm) Core v3.4 - Java Version
* http://www.heatonresearch.com/encog/
* https://github.com/encog/encog-java-core
* Copyright 2008-2016 Heaton Research, Inc.
*
* 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.
*
* For more information on Heaton Research copyrights, licenses
* and trademarks visit:
* http://www.heatonresearch.com/copyright
*/
package org.encog.ml.hmm.distributions;
import java.util.Arrays;
import java.util.Random;
import org.encog.mathutil.matrices.Matrix;
import org.encog.mathutil.matrices.MatrixMath;
import org.encog.mathutil.matrices.decomposition.CholeskyDecomposition;
import org.encog.ml.data.MLDataPair;
import org.encog.ml.data.MLDataSet;
import org.encog.ml.data.basic.BasicMLData;
import org.encog.ml.data.basic.BasicMLDataPair;
import org.encog.util.EngineArray;
/**
* A continuous distribution represents an infinite range of choices between two
* real numbers. A gaussian distribution is used to distribute the probability.
*
*/
public class ContinousDistribution implements StateDistribution {
/**
* The serial id.
*/
private static final long serialVersionUID = 1L;
/**
* The dimensions.
*/
final private int dimension;
/**
* The means for each dimension.
*/
final private double[] mean;
/**
* The covariance matrix.
*/
final private Matrix covariance;
/**
* The covariance left side.
*/
private Matrix covarianceL = null;
/**
* The covariance inverse.
*/
private Matrix covarianceInv = null;
/**
* The covariance determinant.
*/
private double covarianceDet;
/**
* Random number generator.
*/
private final static Random randomGenerator = new Random();
/**
* Used to perform a decomposition.
*/
private CholeskyDecomposition cd;
/**
* Construct a continuous distribution.
* @param mean The mean.
* @param covariance The covariance.
*/
public ContinousDistribution(final double[] mean,
final double[][] covariance) {
this.dimension = covariance.length;
this.mean = EngineArray.arrayCopy(mean);
this.covariance = new Matrix(covariance);
update(covariance);
}
/**
* Construct a continuous distribution with the specified number of dimensions.
* @param dimension The dimensions.
*/
public ContinousDistribution(final int dimension) {
if (dimension <= 0) {
throw new IllegalArgumentException();
}
this.dimension = dimension;
this.mean = new double[dimension];
this.covariance = new Matrix(dimension, dimension);
}
/**
* {@inheritDoc}
*/
@Override
public ContinousDistribution clone() {
try {
return (ContinousDistribution) super.clone();
} catch (final CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void fit(final MLDataSet co) {
final double[] weights = new double[co.size()];
Arrays.fill(weights, 1. / co.size());
fit(co, weights);
}
/**
* {@inheritDoc}
*/
@Override
public void fit(final MLDataSet co, final double[] weights) {
if ((co.size() < 1) || (co.size() != weights.length)) {
throw new IllegalArgumentException();
}
// Compute mean
final double[] mean = new double[this.dimension];
for (int r = 0; r < this.dimension; r++) {
int i = 0;
for (final MLDataPair o : co) {
mean[r] += o.getInput().getData(r) * weights[i++];
}
}
// Compute covariance
final double[][] covariance = new double[this.dimension][this.dimension];
int i = 0;
for (final MLDataPair o : co) {
final double[] obs = o.getInput().getData();
final double[] omm = new double[obs.length];
for (int j = 0; j < obs.length; j++) {
omm[j] = obs[j] - mean[j];
}
for (int r = 0; r < this.dimension; r++) {
for (int c = 0; c < this.dimension; c++) {
covariance[r][c] += omm[r] * omm[c] * weights[i];
}
}
i++;
}
update(covariance);
}
/**
* {@inheritDoc}
*/
@Override
public MLDataPair generate() {
final double[] d = new double[this.dimension];
for (int i = 0; i < this.dimension; i++) {
d[i] = ContinousDistribution.randomGenerator.nextGaussian();
}
final double[] d2 = MatrixMath.multiply(this.covarianceL, d);
return new BasicMLDataPair(new BasicMLData(EngineArray.add(d2,
this.mean)));
}
/**
* {@inheritDoc}
*/
@Override
public double probability(final MLDataPair o) {
final double[] v = o.getInputArray();
final Matrix vmm = Matrix.createColumnMatrix(EngineArray.subtract(v,
this.mean));
final Matrix t = MatrixMath.multiply(this.covarianceInv, vmm);
final double expArg = MatrixMath.multiply(MatrixMath.transpose(vmm), t)
.get(0, 0) * -0.5;
return Math.exp(expArg)
/ (Math.pow(2.0 * Math.PI, this.dimension / 2.0) * Math.pow(
this.covarianceDet, 0.5));
}
/**
* Update the covariance.
* @param covariance The new covariance.
*/
public void update(final double[][] covariance) {
this.cd = new CholeskyDecomposition(new Matrix(covariance));
this.covarianceL = this.cd.getL();
this.covarianceInv = this.cd.inverseCholesky();
this.covarianceDet = this.cd.getDeterminant();
}
/**
* @return The mean for the dimensions of the gaussian curve.
*/
public double[] getMean() {
return this.mean;
}
/**
* @return The covariance matrix.
*/
public Matrix getCovariance() {
return this.covariance;
}
}