All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.sandia.cognition.statistics.distribution.MultivariateStudentTDistribution Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * File:                MultivariateStudentTDistribution.java
 * Authors:             Kevin R. Dixon
 * Company:             Sandia National Laboratories
 * Project:             Cognitive Foundry
 * 
 * Copyright Mar 29, 2010, Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the U.S. Government.
 * Export of this program may require a license from the United States
 * Government. See CopyrightHistory.txt for complete details.
 * 
 */

package gov.sandia.cognition.statistics.distribution;

import gov.sandia.cognition.annotation.PublicationReference;
import gov.sandia.cognition.annotation.PublicationReferences;
import gov.sandia.cognition.annotation.PublicationType;
import gov.sandia.cognition.math.MathUtil;
import gov.sandia.cognition.math.matrix.Matrix;
import gov.sandia.cognition.math.matrix.MatrixFactory;
import gov.sandia.cognition.math.matrix.Vector;
import gov.sandia.cognition.math.matrix.VectorFactory;
import gov.sandia.cognition.math.matrix.VectorInputEvaluator;
import gov.sandia.cognition.statistics.AbstractDistribution;
import gov.sandia.cognition.statistics.ClosedFormComputableDistribution;
import gov.sandia.cognition.statistics.ProbabilityDensityFunction;
import gov.sandia.cognition.util.ObjectUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;

/**
 * Multivariate generalization of the noncentral Student's t-distribution.
 * @author Kevin R. Dixon
 * @since 3.0
 */
@PublicationReferences(
    references={
        @PublicationReference(
            author="Christopher M. Bishop",
            title="Pattern Recognition and Machine Learning",
            type=PublicationType.Book,
            year=2006,
            pages={104,105}
        )
        ,
        @PublicationReference(
            author="Wikipedia",
            title="Multivariate Student distribution",
            type=PublicationType.WebPage,
            year=2010,
            url="http://en.wikipedia.org/wiki/Multivariate_Student_distribution"
        )
    }
)
public class MultivariateStudentTDistribution 
    extends AbstractDistribution
    implements ClosedFormComputableDistribution
{

    /**
     * Default dimensionality, {@value}.
     */
    public static final int DEFAULT_DIMENSIONALITY = 2;

    /**
     * Default degrees of freedom, {@value}.
     */
    public static final double DEFAULT_DEGREES_OF_FREEDOM = 3.0;

    /**
     * Degrees of freedom in the distribution, usually the number of
     * datapoints - 1, DOFs must be greater than zero.
     */
    protected double degreesOfFreedom;

    /**
     * Mean, or noncentrality parameter, of the distribution
     */
    protected Vector mean;

    /**
     * Precision, which is proportionate to the inverse of variance, of the
     * distribution, must be symmetric and positive definite.
     */
    private Matrix precision;

    /** 
     * Creates a new instance of MultivariateStudentTDistribution 
     */
    public MultivariateStudentTDistribution()
    {
        this( DEFAULT_DIMENSIONALITY );
    }

    /**
     * Creates a distribution with the given dimensionality.
     * @param dimensionality
     * Dimensionality of the distribution.
     */
    public MultivariateStudentTDistribution(
        int dimensionality )
    {
        this( DEFAULT_DEGREES_OF_FREEDOM,
            VectorFactory.getDefault().createVector(dimensionality),
            MatrixFactory.getDefault().createIdentity(dimensionality,dimensionality) );
    }

    /**
     * Creates a distribution with the given dimensionality.
     * @param degreesOfFreedom
     * Degrees of freedom in the distribution, usually the number of
     * datapoints - 1, DOFs must be greater than zero.
     * @param mean
     * Mean, or noncentrality parameter, of the distribution
     * @param precision
     * Precision, which is proportionate to the inverse of variance, of the
     * distribution, must be symmetric and positive definite.
     */
    public MultivariateStudentTDistribution(
        double degreesOfFreedom,
        Vector mean,
        Matrix precision)
    {
        this.degreesOfFreedom = degreesOfFreedom;
        this.mean = mean;
        this.precision = precision;
    }

    /**
     * Copy constructor
     * @param other
     * MultivariateStudentTDistribution to copy
     */
    public MultivariateStudentTDistribution(
        MultivariateStudentTDistribution other )
    {
        this( other.getDegreesOfFreedom(),
            ObjectUtil.cloneSafe( other.getMean() ),
            ObjectUtil.cloneSafe( other.getPrecision() ) );
    }

    @Override
    public MultivariateStudentTDistribution clone()
    {
        MultivariateStudentTDistribution clone =
            (MultivariateStudentTDistribution) super.clone();
        clone.setMean( ObjectUtil.cloneSafe(this.getMean() ) );
        clone.setPrecision( ObjectUtil.cloneSafe( this.getPrecision() ) );
        return clone;
    }

    /**
     * Getter for degreesOfFreedom
     * @return 
     * Degrees of freedom in the distribution, usually the number of
     * datapoints - 1, DOFs must be greater than zero.
     */
    public double getDegreesOfFreedom()
    {
        return this.degreesOfFreedom;
    }

    /**
     * Setter for degreesOfFreedom
     * @param degreesOfFreedom
     * Degrees of freedom in the distribution, usually the number of
     * datapoints - 1, DOFs must be greater than zero.
     */
    public void setDegreesOfFreedom(
        double degreesOfFreedom)
    {
        if( degreesOfFreedom <= 0.0 )
        {
            throw new IllegalArgumentException(
                "DOFs must be > 0.0" );
        }
        this.degreesOfFreedom = degreesOfFreedom;
    }

    public Vector getMean()
    {
        return this.mean;
    }

    /**
     * Setter for mean
     * @param mean
     * Mean, or noncentrality parameter, of the distribution
     */
    public void setMean(
        Vector mean)
    {
        this.mean = mean;
    }

    /**
     * Getter for precision
     * @return
     * Precision, which is proportionate to the inverse of variance, of the
     * distribution, must be symmetric and positive definite.
     */
    public Matrix getPrecision()
    {
        return this.precision;
    }

    /**
     * Setter for precision
     * @param precision
     * Precision, which is proportionate to the inverse of variance, of the
     * distribution, must be symmetric and positive definite.
     */
    public void setPrecision(
        Matrix precision)
    {
        if( !precision.isSymmetric() )
        {
            throw new IllegalArgumentException(
                "Precision must be symmetric and positive definite!" );
        }
        this.precision = precision;
    }

    /**
     * Computes the covariance of the distribution, which involves inverting
     * the precision matrix.
     * @return
     * covariance of the distribution, which involves inverting the
     * precision matrix.
     */
    public Matrix getCovariance()
    {
        final double scale = this.degreesOfFreedom / (this.degreesOfFreedom-2.0);
        Matrix C = this.getPrecision().inverse();
        C.scaleEquals(scale);
        return C;
    }

    @Override
    public void sampleInto(
        final Random random,
        final int sampleCount,
        final Collection output)
    {
        final int dim = this.getInputDimensionality();
        Vector zeroMean = VectorFactory.getDefault().createVector(dim);

        MultivariateGaussian mvg = new MultivariateGaussian(
            zeroMean, this.precision.inverse() );
        double[] Vs = ChiSquareDistribution.sampleAsDoubles(
            this.degreesOfFreedom, random, sampleCount);
        ArrayList Zs = mvg.sample(random, sampleCount);
        for( int n = 0; n < sampleCount; n++ )
        {
            Vector z = Zs.get(n);
            double v = Vs[n];
            z.scaleEquals( Math.sqrt(this.degreesOfFreedom/v) );
            z.plusEquals(this.mean);
            output.add( z );
        }
    }

    public Vector convertToVector()
    {
        Vector parameters = VectorFactory.getDefault().copyValues(
            this.getDegreesOfFreedom() );
        parameters = parameters.stack( this.getMean() );
        parameters = parameters.stack( this.getPrecision().convertToVector() );
        return parameters;
    }

    public void convertFromVector(
        Vector parameters)
    {
        final int dim = this.getInputDimensionality();
        parameters.assertDimensionalityEquals(1+dim+dim*dim);
        this.setDegreesOfFreedom( parameters.getElement(0) );
        this.setMean( parameters.subVector(1, dim) );
        Matrix p = this.getPrecision();
        p.convertFromVector( parameters.subVector(
            dim+1, parameters.getDimensionality()-1) );
        this.setPrecision(p);
    }

    /**
     * Gets the dimensionality of the distribution.
     * @return
     * Dimensionality of the distribution.
     */
    public int getInputDimensionality()
    {
        return this.getMean().getDimensionality();
    }

    public MultivariateStudentTDistribution.PDF getProbabilityFunction()
    {
        return new MultivariateStudentTDistribution.PDF( this );
    }

    /**
     * PDF of the MultivariateStudentTDistribution
     */
    public static class PDF
        extends MultivariateStudentTDistribution
        implements ProbabilityDensityFunction,
        VectorInputEvaluator
    {

        /**
         * Log determinant of the precision matrix.
         */
        private Double logDeterminantPrecision;

        /**
         * Creates a new instance of MultivariateStudentTDistribution
         */
        public PDF()
        {
            super();
        }

        /**
         * Creates a distribution with the given dimensionality.
         * @param dimensionality
         * Dimensionality of the distribution.
         */
        public PDF(
            int dimensionality )
        {
            super( dimensionality );
        }

        /**
         * Creates a distribution with the given dimensionality.
         * @param degreesOfFreedom
         * Degrees of freedom in the distribution, usually the number of
         * datapoints - 1, DOFs must be greater than zero.
         * @param mean
         * Mean, or noncentrality parameter, of the distribution
         * @param precision
         * Precision, which is proportionate to the inverse of variance, of the
         * distribution, must be symmetric and positive definite.
         */
        public PDF(
            double degreesOfFreedom,
            Vector mean,
            Matrix precision)
        {
            super( degreesOfFreedom, mean, precision );
        }

        /**
         * Copy constructor
         * @param other
         * MultivariateStudentTDistribution to copy
         */
        public PDF(
            MultivariateStudentTDistribution other )
        {
            super( other );
        }

        @Override
        public MultivariateStudentTDistribution.PDF getProbabilityFunction()
        {
            return this;
        }

        public double logEvaluate(
            Vector input)
        {
            final int dim = this.getInputDimensionality();
            final double logDet = this.getLogDeterminantPrecision();
            final Vector delta = input.minus(this.mean);
            final double z2 = delta.times(this.getPrecision()).dotProduct(delta);
            final double d2pv2 = dim/2.0+this.degreesOfFreedom/2.0;
            double logSum = 0.0;
            logSum += MathUtil.logGammaFunction( d2pv2 );
            logSum -= MathUtil.logGammaFunction( this.degreesOfFreedom/2.0 );
            logSum += 0.5 * logDet;
            logSum -= (dim/2.0)*Math.log(Math.PI*this.degreesOfFreedom);
            logSum -= d2pv2*Math.log( 1.0 + z2/this.degreesOfFreedom );
            return logSum;
        }

        public Double evaluate(
            Vector input)
        {
            return Math.exp( this.logEvaluate(input) );
        }

        /**
         * Getter for logDeterminantPrecision
         * @return
         * Log determinant of the precision matrix.
         */
        public Double getLogDeterminantPrecision()
        {
            if( this.logDeterminantPrecision == null )
            {
                this.logDeterminantPrecision =
                    this.getPrecision().logDeterminant().getRealPart();
            }
            return this.logDeterminantPrecision;
        }

        @Override
        public void setPrecision(
            Matrix precision)
        {
            this.logDeterminantPrecision = null;
            super.setPrecision(precision);
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy