![JAR search and dependency download from the Maven repository](/logo.png)
gov.sandia.cognition.statistics.TransferEntropy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cognitive-foundry Show documentation
Show all versions of cognitive-foundry Show documentation
A single jar with all the Cognitive Foundry components.
/*
* File: TransferEntropy.java
* Authors: Tom Brounstein
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright April 2, 2014, 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;
import gov.sandia.cognition.annotation.PublicationReference;
import gov.sandia.cognition.annotation.PublicationType;
import gov.sandia.cognition.math.matrix.Vector;
import gov.sandia.cognition.statistics.bayesian.ConditionalProbability;
import gov.sandia.cognition.statistics.distribution.DefaultDataDistribution;
import gov.sandia.cognition.util.DefaultPair;
import gov.sandia.cognition.util.Pair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* A class for calculating the transfer entropy of two vectors. Note that in the
* current implementation only values included in a transfer entropy object are
* used in calculating the conditional probability. For example: assume the two
* vectors were [0,0,0,0,1] and [1,1,1,1,1] with window size 1. Then, when
* computing the conditional probabilities, the state [1,1] will not be included
* in the calculations since there's no triad that includes [1,1] in the prior.
* This generates more intuitive results (the above example has a transfer entropy value of 0
* in this implementation), and is easier to code.
* @author trbroun
* @since 3.4.2
*/
@PublicationReference(
author="Thomas Schreiber",
title="Measuring Information Transfer",
type=PublicationType.Journal,
year=2000,
url="http://journals.aps.org/prl/pdf/10.1103/PhysRevLett.85.461"
)
public class TransferEntropy{
private final DefaultDataDistribution oneTermPriorDistribution;
private final DefaultDataDistribution oneTermPosteriorDistribution;
private final DefaultDataDistribution twoTermPriorDistribution;
private final DefaultDataDistribution twoTermPosteriorDistribution;
/**
* Creates a new transfer entropy object
* @param destinationVector
* @param sourceVector
* @param windowSize How many steps in the past will be used in the analysis.
*/
public TransferEntropy(Vector sourceVector, Vector destinationVector,
int windowSize) {
this(sourceVector, destinationVector, windowSize, windowSize);
}
/**
* Creates a new transfer entropy object.
* @param destinationVector
* @param sourceVector
* @param destinationWindowSize How many time steps into the past will be used with the vectorBeingActedOn
* @param sourceWindowSize How many time steps into the past will be sued with the vectorActingOnOtherVector
*/
public TransferEntropy(Vector sourceVector, Vector destinationVector,
int sourceWindowSize, int destinationWindowSize) {
oneTermPriorDistribution = new DefaultDataDistribution();
twoTermPriorDistribution = new DefaultDataDistribution();
oneTermPosteriorDistribution = new DefaultDataDistribution();
twoTermPosteriorDistribution = new DefaultDataDistribution();
//Ensure window sizes are at least 1.
destinationWindowSize = Math.max(destinationWindowSize, 1);
sourceWindowSize = Math.max(sourceWindowSize, 1);
int maxWindow = Math.max(destinationWindowSize, sourceWindowSize);
//Generates the distributions by iterating over the vectors.
for (int ii=0; ii < destinationVector.getDimensionality() - maxWindow; ii++) {
ArrayList destinationVectorPrior = new ArrayList(destinationWindowSize);
addVectorElements(destinationVector, ii + maxWindow - destinationWindowSize, ii + maxWindow, destinationVectorPrior);
ArrayList sourceVectorPrior = new ArrayList(sourceWindowSize);
addVectorElements(sourceVector, ii + maxWindow - sourceWindowSize, ii + maxWindow, sourceVectorPrior);
Integer posterior = (int) destinationVector.getElement(ii + maxWindow);
oneTermPriorDistribution.increment(new TransferEntropyDistributionObject(destinationVectorPrior, null, null));
twoTermPriorDistribution.increment(new TransferEntropyDistributionObject(destinationVectorPrior, sourceVectorPrior, null));
oneTermPosteriorDistribution.increment(new TransferEntropyDistributionObject(destinationVectorPrior, null, posterior));
twoTermPosteriorDistribution.increment(new TransferEntropyDistributionObject(destinationVectorPrior, sourceVectorPrior, posterior));
}
}
/**
* Creates a new transfer entropy object using lists instead of vectors.
* @param destinationList
* @param sourceList
* @param windowSize How many steps in the past will be used in the analysis.
*/
public TransferEntropy(List extends Object> sourceList, List extends Object> destinationList,
int windowSize) {
this(sourceList, destinationList, windowSize, windowSize);
}
/**
* Creates a new transfer entropy object using lists instead of vectors.
* @param destinationList
* @param sourceList
* @param destinationWindowSize How many time steps into the past will be used with the vectorBeingActedOn
* @param sourceWindowSize How many time steps into the past will be sued with the vectorActingOnOtherVector
*/
public TransferEntropy(List extends Object> sourceList, List extends Object> destinationList,
int sourceWindowSize, int destinationWindowSize) {
oneTermPriorDistribution = new DefaultDataDistribution();
twoTermPriorDistribution = new DefaultDataDistribution();
oneTermPosteriorDistribution = new DefaultDataDistribution();
twoTermPosteriorDistribution = new DefaultDataDistribution();
//Ensure window sizes are at least 1.
destinationWindowSize = Math.max(destinationWindowSize, 1);
sourceWindowSize = Math.max(sourceWindowSize, 1);
int maxWindow = Math.max(destinationWindowSize, sourceWindowSize);
//Generates the distributions by iterating over the vectors.
for (int ii=0; ii < destinationList.size() - maxWindow; ii++) {
List extends Object> destinationListPrior = destinationList.subList(
ii + maxWindow - destinationWindowSize, ii + maxWindow);
List extends Object> sourceListPrior = sourceList.subList(ii + maxWindow - sourceWindowSize,
ii + maxWindow);
Object posterior = destinationList.get(ii + maxWindow);
oneTermPriorDistribution.increment(new TransferEntropyDistributionObject(destinationListPrior, null, null));
twoTermPriorDistribution.increment(new TransferEntropyDistributionObject(destinationListPrior, sourceListPrior, null));
oneTermPosteriorDistribution.increment(new TransferEntropyDistributionObject(destinationListPrior, null, posterior));
twoTermPosteriorDistribution.increment(new TransferEntropyDistributionObject(destinationListPrior, sourceListPrior, posterior));
}
}
/**
* Creates a new transfer entropy object using distributions.
* @param oneTermPriorDistribution The prior distribution over one vector. The prior one term prior should be filled in,
* while the second vector prior and posterior should be null (or a consistent value over each element of the domain).
* @param oneTermPosteriorDistribution A distribution over the first term prior and the posterior. Leave the second term vector prior
* as null, or a consistent value across the domain space.
* @param twoTermPriorDistribution A distribution over TransferEntropyDistributionObjects where the prior for the first and second vector is supplied.
* @param twoTermPosteriorDistribution A distribution over TransferEntropyDistributionObjects where all values are supplied.
*/
public TransferEntropy(DefaultDataDistribution oneTermPriorDistribution,
DefaultDataDistribution oneTermPosteriorDistribution,
DefaultDataDistribution twoTermPriorDistribution,
DefaultDataDistribution twoTermPosteriorDistribution) {
this.oneTermPriorDistribution = oneTermPriorDistribution;
this.oneTermPosteriorDistribution = oneTermPosteriorDistribution;
this.twoTermPriorDistribution = twoTermPriorDistribution;
this.twoTermPosteriorDistribution = twoTermPosteriorDistribution;
}
/**
* Computes the transfer entropy value.
* @return The transfer entropy from one vector to the other, or one distribution to the other.
*/
public double compute() {
double solution = 0.0;
ConditionalProbability instance = new ConditionalProbability();
for (TransferEntropyDistributionObject state : twoTermPosteriorDistribution.getDomain()) {
double answer = partialSum(state, instance);
solution += answer;
}
return solution;
}
/**
* Computes the transfer entropy value and returns a map of the states with their corresponding partial sums.
* Used to identify which states contribute the most or least to the transfer entropy.
* @return A pair, where the first element is the transfer entropy value between the two vectors, and the second
* element is a map from states to their corresponding partial sums.
*/
public Pair> computeWithPartialSums() {
double solution = 0.0;
HashMap mapSolution =
new HashMap();
ConditionalProbability instance =
new ConditionalProbability();
for (TransferEntropyDistributionObject state : twoTermPosteriorDistribution.getDomain()) {
TransferEntropyPartialSumObject answerWithWrapper = partialSumWithWrapper(state, instance);
solution += answerWithWrapper.partialSum;
mapSolution.put(state, answerWithWrapper);
}
Pair> finalAnswer =
new DefaultPair>(solution, mapSolution);
return finalAnswer;
}
/**
* A helper method to add values from the vector to an array list, and transform them to integers.
* Gets all elements in range start to finish. Is 0 indexed.
* @param vector The vector where elements are being taken from.
* @param start The start index for elements to be copied from the vector.
* @param finish The end index for elements to be copied from the vector.
* @param temp The ArrayList that has elements being added to it.
*/
private void addVectorElements(Vector vector, int start, int finish, ArrayList temp) {
for (int ii=start; ii < finish; ii++) {
temp.add((int) vector.getElement(ii));
}
}
/**
* A helper method that computes an individual term in the summation.
* @param state The TransferEntropyDistributionObjects state being analyzed in this element of the sum.
* @param instance A conditional probability object, used to calculate elements of the transfer entropy.
* @return The transfer entropy partial sum for this specific state.
*/
private double partialSum(TransferEntropyDistributionObject state, ConditionalProbability instance) {
//Note that numerator and denominator can never be 0 (if they are, there's a bug in the code).
//At this point in the code we're itereating over all observed two vector posterior states,
//which take the form i_(n+1), i_n^k, j_n^l. The numerator looks at probability of i_(n+1)
//given i_n^k and j_n^k; this probability is greater than 0 since we know there is at least
//one case of i_(n+1), i_n^k, j_n^l is seen since it's included in the loop. The denominator
//follows similarly. Thus, both of these values are non-0.
double numerator = instance.computeConditionalProbabilityWhenDataTypeHasHistoricalData(
new TransferEntropyDistributionObject(state.destinationVectorPrior, state.sourceVectorPrior), state,
twoTermPriorDistribution, twoTermPosteriorDistribution);
double denominator = instance.computeConditionalProbabilityWhenDataTypeHasHistoricalData(
new TransferEntropyDistributionObject(state.destinationVectorPrior), new TransferEntropyDistributionObject(state.destinationVectorPrior, state.posterior),
oneTermPriorDistribution, oneTermPosteriorDistribution);
double multiple = twoTermPosteriorDistribution.getProbabilityFunction().evaluate(
state);
double answer = multiple * Math.log(numerator/denominator);
//The return statement could have been in the previous statement, but this is to be explicit.
return answer;
}
/**
* A helper method that wraps a transfer entropy partial sum value with extra information.
* @param state The state being evaluated.
* @param instance A conditional probability object, used to calculate elements of the transfer entropy.
* @return An object which includes the state, the partial sum value, and how many times the state occurs.
*/
private TransferEntropyPartialSumObject partialSumWithWrapper(TransferEntropyDistributionObject state, ConditionalProbability instance) {
double answer = partialSum(state, instance);
double numberOfAppearances = twoTermPosteriorDistribution.get(state);
return new TransferEntropyPartialSumObject(state, answer, numberOfAppearances);
}
/**
* A helper class that define the objects used by the distributions in transfer entropy.
*/
public static class TransferEntropyDistributionObject{
/** The prior for the destination vector. */
public final List extends Object> destinationVectorPrior;
/** The prior for the source vector. */
public final List extends Object> sourceVectorPrior;
/** The posterior. */
public final Object posterior;
/**
* Basic constructor for a TransferEntropyDistributionObjects. FirstVectPrior is assigned in the obvious manner, all other values null.
* @param destinationVectorPrior
*/
public TransferEntropyDistributionObject(List extends Object> destinationVectorPrior) {
this(destinationVectorPrior, null, null);
}
/**
* Basic constructor for a TransferEntropyDistributionObjects. First and second vector priors are assigned in the obvious manner, posterior is null.
* @param destinationVectorPrior
* @param sourceVectorPrior
*/
public TransferEntropyDistributionObject(List extends Object> destinationVectorPrior, List extends Object> sourceVectorPrior) {
this(destinationVectorPrior, sourceVectorPrior, null);
}
/**
* Basic constructor for a TransferEntropyDistributionObjects. First vector prior and posterior are assigned in the obvious manner, second vector prior is null.
* @param destinationVectorPrior
* @param posterior
*/
public TransferEntropyDistributionObject(List extends Object> destinationVectorPrior, Object posterior) {
this(destinationVectorPrior, null, posterior);
}
/**
* Basic constructor for a TransferEntropyDistributionObjects. All values are assigned in the obvious manner.
* @param destinationVectorPrior
* @param sourceVectorPrior
* @param posterior
*/
public TransferEntropyDistributionObject(List extends Object> destinationVectorPrior, List extends Object> sourceVectorPrior, Object posterior) {
this.destinationVectorPrior = destinationVectorPrior;
this.sourceVectorPrior = sourceVectorPrior;
this.posterior = posterior;
}
@Override
public boolean equals(Object other) {
if (other instanceof TransferEntropyDistributionObject) {
return equals((TransferEntropyDistributionObject) other);
}
return false;
}
/**
* Equals function for comparing two TransferEntropyDistributionObjects.
* @param other The other TransferEntropyDistributionObjects being compared.
* @return If the two objects are equal. Compares each component individually.
*/
private boolean equals(TransferEntropyDistributionObject other) {
return compareComponent(destinationVectorPrior, other.destinationVectorPrior) &&
compareComponent(sourceVectorPrior, other.sourceVectorPrior) &&
compareComponent(posterior, other.posterior);
}
/**
* Compares two objects to see if they're both null. Otherwise, checks if they're equal.
* @param first
* @param second
* @return If the two objects are both null or equal.
*/
private boolean compareComponent(Object first, Object second) {
if (first==null) {
return second==null;
}
return first.equals(second);
}
@Override
public int hashCode() {
ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy