Made sure all discrete calculators use log_2 to make answers in bits (some were still using log of base).

Merged usual and debug methods for computing average MI in discrete calculator

Added setDebug to super InfoMeasureCalculator

Cleaned up header of InfoMeasureCalculator

Adding TODO comments for Kraskov calculators

Bug fix on MutualInfoCalculatorMultiVariateWithDiscreteKraskov - we weren't normalising incoming observations for computing local MI properly, this is fixed now.
This commit is contained in:
joseph.lizier 2012-09-05 06:23:48 +00:00
parent d9b6393a06
commit cda8689afb
13 changed files with 171 additions and 66 deletions

View File

@ -110,7 +110,7 @@ public interface MutualInfoCalculatorMultiVariateWithDiscrete {
*
* @param numPermutationsToCheck the number of permuted surrogates to examine
* @return the proportion of MI scores from the distribution which have higher or equal MIs to ours.
* @link "Chavez et. al., 'Statistical assessment of nonlinear causality:
* @see "Chavez et. al., 'Statistical assessment of nonlinear causality:
* application to epileptic EEG signals', Journal of Neuroscience Methods 124 (2003) 113-128"
* @throws Exception
*/

View File

@ -228,7 +228,9 @@ public abstract class ConditionalMutualInfoCalculatorMultiVariateKraskov {
public abstract double[] computeLocalOfPreviousObservations() throws Exception;
public double[] computeLocalUsingPreviousObservations(double[][] states1, double[][] states2) throws Exception {
// If implemented, will need to incorporate any time difference here.
// If implemented, will need to incorporate any normalisation here
// (normalising the incoming data the same way the previously
// supplied observations were normalised).
throw new Exception("Local method not implemented yet");
}

View File

@ -541,6 +541,10 @@ public class ConditionalMutualInfoCalculatorMultiVariateWithDiscreteKraskov impl
public double[] computeLocalUsingPreviousObservations(double[][] continuousStates,
int[] discreteStates) throws Exception {
// TODO Implement local method.
// Note: will need to keep the means and stds of supplied observations
// if we normalised them (since we'll need to normalise the
// observations supplied here to match them)
throw new Exception("Local method not implemented yet");
}
@ -560,6 +564,9 @@ public class ConditionalMutualInfoCalculatorMultiVariateWithDiscreteKraskov impl
double[][] contStates, int[] discreteStates,
double[][] conditionedStates) throws Exception {
// TODO Auto-generated method stub
// Note: will need to keep the means and stds of supplied observations
// if we normalised them (since we'll need to normalise the
// observations supplied here to match them)
throw new Exception("Not implemented yet");
}
}

View File

@ -244,6 +244,9 @@ public abstract class MultiInfoCalculatorKraskov implements
public abstract double[] computeLocalOfPreviousObservations() throws Exception;
public double[] computeLocalUsingPreviousObservations(double[][] states) throws Exception {
// TODO If this is implemented, will need to normalise the incoming
// observations the same way that previously supplied ones were
// normalised (if they were normalised, that is)
throw new Exception("Local method not implemented yet");
}

View File

@ -229,7 +229,9 @@ public abstract class MutualInfoCalculatorMultiVariateKraskov implements
public abstract double[] computeLocalOfPreviousObservations() throws Exception;
public double[] computeLocalUsingPreviousObservations(double[][] states1, double[][] states2) throws Exception {
// If implemented, will need to incorporate any time difference here.
// TODO If implemented, will need to incorporate any time difference here.
// Will also need to handle normalisation of the incoming data
// appropriately
throw new Exception("Local method not implemented yet");
}

View File

@ -328,6 +328,8 @@ public abstract class MutualInfoCalculatorMultiVariateKraskovByMulti implements
}
public double[] computeLocalUsingPreviousObservations(double[][] states1, double[][] states2) throws Exception {
// TODO When implemented, we'll need to normalise the data in the same way
// as previously supplied observations (if they were normalised here)
throw new Exception("Local method not implemented yet");
}

View File

@ -35,7 +35,14 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
protected double[][] continuousData;
protected int[] discreteData;
protected int[] counts;
/**
* Number of possible states of the discrete variable
*/
protected int base;
/**
* Number of dimenions of the joint continuous variable
*/
protected int dimensions;
protected boolean debug;
protected double mi;
protected boolean miComputed;
@ -50,7 +57,19 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
public final static String PROP_K = "k";
public final static String PROP_NORM_TYPE = "NORM_TYPE";
public static final String PROP_NORMALISE = "NORMALISE";
private boolean normalise = true;
/**
* Track whether we're going to normalise the joint variables individually
*/
protected boolean normalise = true;
/**
* Track the means of the joint variables if we are normalising them
*/
protected double[] means;
/**
* Track the std devs of the joint variables if we are normalising them
*/
protected double[] stds;
public MutualInfoCalculatorMultiVariateWithDiscreteKraskov() {
super();
@ -68,8 +87,10 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
miComputed = false;
xNorms = null;
continuousData = null;
means = null;
stds = null;
discreteData = null;
// No need to keep the dimenions here
this.dimensions = dimensions;
this.base = base;
}
@ -122,11 +143,18 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
if (continuousObservations[0].length == 0) {
throw new Exception("Computing MI with a null set of data");
}
if (continuousObservations[0].length != dimensions) {
throw new Exception("The continuous observations do not have the expected number of variables (" + dimensions + ")");
}
continuousData = continuousObservations;
discreteData = discreteObservations;
if (normalise) {
// Take a copy since we're going to normalise it
continuousData = MatrixUtils.normaliseIntoNewArray(continuousObservations);
// And we need to keep the means/stds ready to normalise local values
// that are supplied later:
means = MatrixUtils.means(continuousObservations);
stds = MatrixUtils.stdDevs(continuousObservations, means);
continuousData = MatrixUtils.normaliseIntoNewArray(continuousObservations, means, stds);
}
// count the discrete states:
counts = new int[base];
@ -237,6 +265,9 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
return computeAverageLocalOfObservationsWhileComputingDistances();
}
// Postcondition: we'll compute the norms before the main loop,
// unless they have already been computed:
if (xNorms == null) {
computeNorms();
}
@ -247,6 +278,8 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
double avNx = 0;
double avNy = 0;
double testSum = 0.0; // Used for debugging prints
for (int t = 0; t < N; t++) {
// Compute eps_x and eps_y for this time step:
// using x norms to all neighbours
@ -269,7 +302,20 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
avNy += n_y;
// And take the digamma before adding into the
// average:
averageDiGammas += MathsUtils.digamma(n_x) + MathsUtils.digamma(n_y);
double localSum = MathsUtils.digamma(n_x) + MathsUtils.digamma(n_y);
averageDiGammas += localSum;
if (debug) {
double localValue = MathsUtils.digamma(k) - 1.0/(double)k - localSum + MathsUtils.digamma(N);
testSum += localValue;
if (dimensions == 1) {
System.out.printf("t=%d: x=%.3f, eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
t, continuousData[t][0], eps_x, n_x, n_y, localValue, testSum);
} else {
System.out.printf("t=%d: eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
t, eps_x, n_x, n_y, localValue, testSum);
}
}
}
averageDiGammas /= (double) N;
if (debug) {
@ -304,6 +350,8 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
double avNx = 0;
double avNy = 0;
double testSum = 0.0; // Used for debugging prints
for (int t = 0; t < N; t++) {
// Compute eps_x and eps_y for this time step:
// First get x norms to all neighbours
@ -335,7 +383,20 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
avNy += n_y;
// And take the digamma before adding into the
// average:
averageDiGammas += MathsUtils.digamma(n_x) + MathsUtils.digamma(n_y);
double localSum = MathsUtils.digamma(n_x) + MathsUtils.digamma(n_y);
averageDiGammas += localSum;
if (debug) {
double localValue = MathsUtils.digamma(k) - 1.0/(double)k - localSum + MathsUtils.digamma(N);
testSum += localValue;
if (dimensions == 1) {
System.out.printf("t=%d: x=%.3f, eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
t, continuousData[t][0], eps_x, n_x, n_y, localValue, testSum);
} else {
System.out.printf("t=%d: eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
t, eps_x, n_x, n_y, localValue, testSum);
}
}
}
averageDiGammas /= (double) N;
if (debug) {
@ -425,7 +486,7 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
* it should be ignored, since the data point was not counted in its
* own counts in the standard version anyway.</p>
*
* <p>The supplied observations are intended to be new observations,
* <p><b>Importantly</b>, the supplied observations are intended to be new observations,
* not those fed in to compute the PDFs from. There would be
* some subtle changes necessary to accomodate computing locals on
* the same data set (e.g. not counting the current point as one
@ -440,11 +501,25 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
public double[] computeLocalUsingPreviousObservations(double[][] continuousNewStates,
int[] discreteNewStates) throws Exception {
if (normalise) {
// The stored observations continuousData have been normalised
// according to their stored means and stds; we need to
// normalise the incoming observations the same way before
// comparing them
continuousNewStates = MatrixUtils.normaliseIntoNewArray(
continuousNewStates, means, stds);
}
int N = continuousNewStates.length; // number of observations
double[] locals = new double[N];
double fixedPartOfLocals = MathsUtils.digamma(k) - 1.0/(double)k +
MathsUtils.digamma(N);
double testSum = 0.0;
if (debug) {
System.out.printf("digamma(k)=%.3f - 1/k=%.3f + digamma(N)=%.3f\n",
MathsUtils.digamma(k), 1.0/(double)k, MathsUtils.digamma(N));
}
double avNx = 0;
double avNy = 0;
@ -476,11 +551,21 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
// Now compute the local value:
locals[t] = fixedPartOfLocals -
MathsUtils.digamma(n_x) - MathsUtils.digamma(n_y);
if (debug) {
testSum += locals[t];
if (dimensions == 1) {
System.out.printf("t=%d: x=%.3f, eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
t, continuousNewStates[t][0], eps_x, n_x, n_y, locals[t], testSum);
} else {
System.out.printf("t=%d: eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
t, eps_x, n_x, n_y, locals[t], testSum);
}
}
}
if (debug) {
avNx /= (double)N;
avNy /= (double)N;
System.out.println(String.format("Average n_x=%.3f, Average n_y=%.3f", avNx, avNy));
System.out.printf("Average n_x=%.3f, Average n_y=%.3f\n", avNx, avNy);
}
return locals;

View File

@ -638,7 +638,7 @@ public class ApparentTransferEntropyCalculator extends ContextOfPastMeasureCalcu
double logTerm = (double) nextPastCount[nextVal][prevVal] /
(double) pastCount[prevVal] /
p_next;
double localValue = Math.log(logTerm) / log_base;
double localValue = Math.log(logTerm) / log_2;
activeCont = (nextPastCount[nextVal][prevVal] /
(double) observations) * localValue;
} else {

View File

@ -1,15 +1,31 @@
package infodynamics.measures.discrete;
/**
* Info theoretic measure calculator base class
* <p>Info theoretic measure calculator base class, providing common functionality
* for user-level measure classes.</p>
*
* Usage:
* 1. Continuous accumulation of observations before computing :
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations() or computeAverageLocalOfObservations()
* 2. Standalone computation from a single set of observations:
* Call: computeLocal() or computeAverageLocal()
* <p>Usage of child classes is intended to follow this general pattern:
* <ol>
* <li>Construct;</li>
* <li>{@link #initialise()};</li>
* <li>Then, either of the following:
* <ol>
* <li>Continuous accumulation of observations before computing, via:
* <ol>
* <li>calling "addObservations()" methods of children
* several times over;</li>
* <li>Compute required quantities, using
* {@link #computeAverageLocalOfObservations()} or
* "computeLocalUsinPreviousObservations()".</li>
* </ol></li>
* <li>Standalone computation from a single set of observations; call:
* "computeLocal()" or "computeAverageLocal()".</li>
* </ol>
* </ol></p>
*
* <p>Note: various functions referred to above (e.g. "addObservations()")
* are not specified here, so that this class can be a superclass
* for both univariate, pairwise and multivariate methods.</p>
*
* @author Joseph Lizier
* joseph.lizier at gmail.com
@ -120,4 +136,19 @@ public abstract class InfoMeasureCalculator {
}
return true;
}
/**
* Compute the average value of the measure from the previously supplied
* observations
*
* @return average value
*/
public abstract double computeAverageLocalOfObservations();
/**
* @param debug the debug status to set
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
}

View File

@ -151,7 +151,7 @@ public class MultiInformationCalculator extends InfoMeasureCalculator {
}
double jointProb = (double) jointCount[jointValue] / (double) observations;
double logValue = jointProb / prodMarginalProbs;
double localValue = Math.log(logValue) / log_base;
double localValue = Math.log(logValue) / log_2;
if (jointProb > 0.0) {
if (!checkedFirst) {
max = localValue;

View File

@ -103,47 +103,9 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
max = 0;
min = 0;
double meanSqLocals = 0;
for (int i = 0; i < base; i++) {
// compute p_i
double probi = (double) iCount[i] / (double) observations;
for (int j = 0; j < base; j++) {
// compute p_j
double probj = (double) jCount[j] / (double) observations;
// compute p(veci=i, vecj=j)
double jointProb = (double) jointCount[i][j] / (double) observations;
// Compute MI contribution:
if (jointProb * probi * probj > 0.0) {
double localValue = Math.log(jointProb / (probi * probj)) / log_base;
miCont = jointProb * localValue;
if (localValue > max) {
max = localValue;
} else if (localValue < min) {
min = localValue;
}
// Add this contribution to the mean
// of the squared local values
meanSqLocals += miCont * localValue;
} else {
miCont = 0.0;
}
mi += miCont;
}
if (debug) {
System.out.println("i\tj\tp_i\tp_j\tp_joint\tlocal");
}
average = mi;
std = Math.sqrt(meanSqLocals - average * average);
return mi;
}
public double debugLocalOfObservations() {
double mi = 0.0;
double miCont = 0.0;
max = 0;
min = 0;
double meanSqLocals = 0;
System.out.println("i\tj\tp_i\tp_j\tp_joint\tlocal");
for (int i = 0; i < base; i++) {
// compute p_i
double probi = (double) iCount[i] / (double) observations;
@ -154,10 +116,12 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
double jointProb = (double) jointCount[i][j] / (double) observations;
// Compute MI contribution:
if (jointProb * probi * probj > 0.0) {
double localValue = Math.log(jointProb / (probi * probj)) / log_base;
double localValue = Math.log(jointProb / (probi * probj)) / log_2;
miCont = jointProb * localValue;
System.out.printf("%d\t%d\t%.4f\t%.4f\t%.4f\t%.4f\n",
i, j, probi, probj, jointProb, localValue);
if (debug) {
System.out.printf("%d\t%d\t%.4f\t%.4f\t%.4f\t%.4f\n",
i, j, probi, probj, jointProb, localValue);
}
if (localValue > max) {
max = localValue;
} else if (localValue < min) {
@ -273,7 +237,7 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
// and we've got two counts on the bottom
// but one count on the top:
logTerm *= (double) observations;
localMI[r] = Math.log(logTerm) / log_base;
localMI[r] = Math.log(logTerm) / log_2;
average += localMI[r];
if (localMI[r] > max) {
max = localMI[r];

View File

@ -1618,7 +1618,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
if (computeMultiInfoCoherence) {
miCalc.setDebug(debug);
}
this.debug = debug;
super.setDebug(debug);
}
}

View File

@ -1838,9 +1838,18 @@ public class MatrixUtils {
* @param matrix 2D matrix of doubles
*/
public static double[][] normaliseIntoNewArray(double[][] matrix) {
double[][] newMatrix = new double[matrix.length][matrix[0].length];
double[] means = means(matrix);
double[] stds = stdDevs(matrix, means);
return normaliseIntoNewArray(matrix, means, stds);
}
/**
* Normalises the elements along each column of the matrix
*
* @param matrix 2D matrix of doubles
*/
public static double[][] normaliseIntoNewArray(double[][] matrix, double[] means, double[] stds) {
double[][] newMatrix = new double[matrix.length][matrix[0].length];
for (int r = 0; r < newMatrix.length; r++) {
for (int c = 0; c < newMatrix[r].length; c++) {
newMatrix[r][c] = matrix[r][c] - means[c];