jidt/java/source/infodynamics/measures/mixed/gaussian/ConditionalMutualInfoCalcul...

231 lines
10 KiB
Java
Executable File

package infodynamics.measures.continuous.gaussian;
import infodynamics.measures.continuous.ConditionalMutualInfoCalculatorMultiVariateWithDiscreteSourceCommon;
import infodynamics.measures.continuous.ConditionalMutualInfoMultiVariateCommon;
import infodynamics.utils.MatrixUtils;
/**
* <p>Computes the differential conditional mutual information of a given multivariate set of
* observations with a discrete variable, conditioned on another multivariate set
* of observations,
* assuming that the probability distribution function for these continuous observations is
* a multivariate Gaussian distribution.</p>
*
* <p>This is done by examining the conditional probability distribution for
* the multivariate continuous variable C (given the discrete
* variable D) against the probability distribution for C, all conditioned
* on another continuous variable Z:
* MI(C;D|Z) := H(C|Z) - H(C|D,Z).</p>
*
* <p><b>CAVEAT EMPTOR</b>: The real question this type of calculation asks
* is to what extent does knowing the value of the discrete variable reduce
* variance in the continuous variable(s), given the other known continuous variable.
* Indeed, it may not to behave (I should test this--)
* as we would normally expect a mutual information calculation: if we add more
* continuous variables in, it may increase in spite of redundancy between these
* variables. TODO Further exploration should take place here ...</p>
*
* <p>
* Usage:
* <ol>
* <li>Construct {@link #ConditionalMutualnfoCalculatorMultiVariateWithDiscreteSourceGaussian()}</li>
* <li>{@link #initialise(int, int, int)}</li>
* <li>Set properties using {@link #setProperty(String, String)}</li>
* <li>Provide the observations to the calculator using:
* {@link #setObservations(double[][], int[], double[][])}, or
* a sequence of:
* {@link #startAddObservations()},
* multiple calls to {@link #addObservations(double[][], int[], double[][])}
* and then
* {@link #finaliseAddObservations()}.</li>
* <li>Compute the required information-theoretic results, primarily:
* {@link #computeAverageLocalOfObservations()} to return the average differential
* entropy based on the variance of
* the supplied observations; or other calls to compute
* local values or statistical significance.</li>
* </ol>
* </p>
*
* <p>
* Alters behaviour slightly from parent class {@link ConditionalMutualInfoMultiVariateCommon}
* in that property {@link ConditionalMutualInfoMultiVariateCommon#PROP_NORMALISE}
* is set to false by default here (since this makes more sense for
* linear-Gaussian analysis).
* </p>
*
* @see <a href="http://mathworld.wolfram.com/DifferentialEntropy.html">Differential entropy for Gaussian random variables at Mathworld</a>
* @see <a href="http://en.wikipedia.org/wiki/Differential_entropy">Differential entropy for Gaussian random variables at Wikipedia</a>
* @see <a href="http://en.wikipedia.org/wiki/Multivariate_normal_distribution">Multivariate normal distribution on Wikipedia</a>
* @author Joseph Lizier joseph.lizier_at_gmail.com
*
*/
public class ConditionalMutualInfoCalculatorMultiVariateWithDiscreteSourceGaussian
extends ConditionalMutualInfoCalculatorMultiVariateWithDiscreteSourceCommon
implements Cloneable {
/**
* Entropy calculator applied to the whole set of conditional data Z
*/
protected EntropyCalculatorMultiVariateGaussian entCalcZ;
/**
* Entropy calculator applied to the whole set of continuous data C
* and conditional data Z
*
*/
protected EntropyCalculatorMultiVariateGaussian entCalcCZ;
/**
* Entropy calculators applied to the set of conditional data Z
* associated with each discrete value
*/
protected EntropyCalculatorMultiVariateGaussian[] entCalcZForEachDiscrete;
/**
* Entropy calculators applied to the set of continuous data C
* and conditional data Z
* associated with each discrete value
*/
protected EntropyCalculatorMultiVariateGaussian[] entCalcCZForEachDiscrete;
public ConditionalMutualInfoCalculatorMultiVariateWithDiscreteSourceGaussian() {
super();
// Normalising data makes less sense for linear-Gaussian estimation,
// so we turn this off by default.
normalise = false;
entCalcZ = new EntropyCalculatorMultiVariateGaussian();
entCalcZForEachDiscrete = null;
entCalcCZ = new EntropyCalculatorMultiVariateGaussian();
entCalcCZForEachDiscrete = null;
}
/* (non-Javadoc)
* @see infodynamics.measures.continuous.ConditionalMutualInfoCalculatorMultiVariateWithDiscreteSourceCommon#initialise(int, int, int)
*/
@Override
public void initialise(int dimensions, int base, int dimensionsCond) {
super.initialise(dimensions, base, dimensionsCond);
entCalcZ.initialise(dimensionsCond);
entCalcCZ.initialise(dimensions + dimensionsCond);
entCalcZForEachDiscrete = new EntropyCalculatorMultiVariateGaussian[base];
entCalcCZForEachDiscrete = new EntropyCalculatorMultiVariateGaussian[base];
for (int b = 0; b < base; b++) {
entCalcZForEachDiscrete[b] = new EntropyCalculatorMultiVariateGaussian();
entCalcCZForEachDiscrete[b] = new EntropyCalculatorMultiVariateGaussian();
// If any properties relevant for these calculators were set in
// setProperty then we should set them here
entCalcZForEachDiscrete[b].initialise(dimensionsCond);
entCalcCZForEachDiscrete[b].initialise(dimensions + dimensionsCond);
}
}
/* (non-Javadoc)
* @see infodynamics.measures.continuous.ConditionalMutualInfoCalculatorMultiVariateWithDiscreteSourceCommon#finaliseAddObservations()
*/
@Override
public void finaliseAddObservations() throws Exception {
super.finaliseAddObservations();
// Set the complete set of observations:
// (this will pick up any errors in the dimensions of the continuous
// observations)
entCalcZ.setObservations(conditionedDataZ);
double[][] joinedCZ = MatrixUtils.appendColumns(continuousDataX, conditionedDataZ);
entCalcCZ.setObservations(joinedCZ);
// Set the observations corresponding to each discrete value:
int totalNumberOfSuppliedObservations = 0;
for (int b = 0; b < base; b++) {
// Extract the observations for when this base value occurs:
double[][] obsZForThisDiscValue = MatrixUtils.extractSelectedPointsMatchingCondition(
conditionedDataZ, discreteData, b);
double[][] obsCZForThisDiscValue = MatrixUtils.extractSelectedPointsMatchingCondition(
joinedCZ, discreteData, b);
// Set the observations for each discrete value:
entCalcZForEachDiscrete[b].setObservations(obsZForThisDiscValue);
entCalcCZForEachDiscrete[b].setObservations(obsCZForThisDiscValue);
totalNumberOfSuppliedObservations += obsZForThisDiscValue.length;
}
// Check that all of the supplied observations were extracted corresponding
// to one of the allowed discrete values
if (totalNumberOfSuppliedObservations != discreteData.length) {
throw new Exception("Some values in discreteObservations were not in the range 0..base-1");
}
}
public double computeAverageLocalOfObservations() throws Exception {
// The average mutual information can be expressed
// as a difference between the conditional entropy of the continuous observations
// and the conditional entropy of the continuous given the
// discrete observations, both given the conditional continuous observations:
// I(C;D|Z) = H(C|Z) - H(C|D,Z)
// = H(C,Z) - H(Z) - H(C,Z|D) + H(C|D)
double meanConditionalEntropyCZ = 0;
double meanConditionalEntropyZ = 0;
for (int b = 0; b < base; b++) {
double pOfB = (double) counts[b] / (double) totalObservations;
meanConditionalEntropyZ += pOfB *
entCalcZForEachDiscrete[b].computeAverageLocalOfObservations();
meanConditionalEntropyCZ += pOfB *
entCalcCZForEachDiscrete[b].computeAverageLocalOfObservations();
}
double entCZ = entCalcCZ.computeAverageLocalOfObservations();
double entZ = entCalcZ.computeAverageLocalOfObservations();
condMi = entCZ - entZ
- meanConditionalEntropyCZ
+ meanConditionalEntropyZ;
if (debug) {
System.out.printf("H(C,Z)=%.4f - H(Z)=%.4f - H(C,Z|D)=%.4f + H(Z|D)=%.4f = %.4f\n",
entCZ, entZ, meanConditionalEntropyCZ, meanConditionalEntropyZ, condMi);
}
return condMi;
}
public double[] computeLocalOfPreviousObservations() throws Exception {
throw new RuntimeException("Not implemented yet");
}
public double[] computeLocalUsingPreviousObservations(
double[][] contStates, int[] discreteStates,
double[][] conditionedStates) throws Exception {
throw new RuntimeException("Not implemented yet");
}
/**
* Clone the object - note: while it does create new cloned instances of
* the {@link EntropyCalculatorMultiVariateGaussian} objects,
* I think these only
* have shallow copies to the data.
* This is enough though to maintain the structure across
* various {@link #computeSignificance(boolean, int)} calls.
*
* @see java.lang.Object#clone()
*/
@Override
protected Object clone() throws CloneNotSupportedException {
ConditionalMutualInfoCalculatorMultiVariateWithDiscreteSourceGaussian theClone =
(ConditionalMutualInfoCalculatorMultiVariateWithDiscreteSourceGaussian) super.clone();
// Now assign clones of the EntropyCalculatorMultiVariateGaussian objects:
// First clone those for the conditional variable:
theClone.entCalcZ =
(EntropyCalculatorMultiVariateGaussian) entCalcZ.clone();
if (entCalcZForEachDiscrete != null) {
theClone.entCalcZForEachDiscrete = new EntropyCalculatorMultiVariateGaussian[base];
for (int b = 0; b < base; b++) {
theClone.entCalcZForEachDiscrete[b] =
(EntropyCalculatorMultiVariateGaussian)
entCalcZForEachDiscrete[b].clone();
}
}
// Next clone those for the conditional variable and the continuous source variable
theClone.entCalcCZ =
(EntropyCalculatorMultiVariateGaussian) entCalcCZ.clone();
if (entCalcCZForEachDiscrete != null) {
theClone.entCalcCZForEachDiscrete = new EntropyCalculatorMultiVariateGaussian[base];
for (int b = 0; b < base; b++) {
theClone.entCalcCZForEachDiscrete[b] =
(EntropyCalculatorMultiVariateGaussian)
entCalcCZForEachDiscrete[b].clone();
}
}
return theClone;
}
}