mirror of https://github.com/jlizier/jidt
486 lines
24 KiB
Java
Executable File
486 lines
24 KiB
Java
Executable File
/*
|
|
* Java Information Dynamics Toolkit (JIDT)
|
|
* Copyright (C) 2012, Joseph T. Lizier
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package infodynamics.measures.continuous.gaussian;
|
|
|
|
import infodynamics.measures.continuous.ConditionalMutualInfoMultiVariateAbstractTester;
|
|
import infodynamics.utils.ArrayFileReader;
|
|
import infodynamics.utils.ChiSquareMeasurementDistribution;
|
|
import infodynamics.utils.MatrixUtils;
|
|
import infodynamics.utils.RandomGenerator;
|
|
|
|
public class ConditionalMutualInfoMultiVariateTester extends
|
|
ConditionalMutualInfoMultiVariateAbstractTester {
|
|
|
|
public void testLocalsAverageCorrectly() throws Exception {
|
|
ConditionalMutualInfoCalculatorMultiVariateGaussian condMiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
super.testLocalsAverageCorrectly(condMiCalc, 2, 100);
|
|
}
|
|
|
|
public void testComputeSignificanceDoesntAlterAverage() throws Exception {
|
|
ConditionalMutualInfoCalculatorMultiVariateGaussian condMiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
super.testComputeSignificanceDoesntAlterAverage(condMiCalc, 2, 100);
|
|
}
|
|
|
|
/**
|
|
* Test the construction of the joint covariance matrix against a known
|
|
* test case verified using covariance calculations in matlab/octave
|
|
*/
|
|
public void testJointCovariance() throws Exception {
|
|
ArrayFileReader afr = new ArrayFileReader("demos/data/4ColsPairedOneStepNoisyDependence-1.txt");
|
|
double[][] data = afr.getDouble2DMatrix();
|
|
|
|
//============================
|
|
// Case 1: Autocovariance conditioned on other variables:
|
|
|
|
double[][] source = MatrixUtils.selectRowsAndColumns(data,
|
|
MatrixUtils.range(0, data.length-2), new int[] {0});
|
|
double[][] dest = MatrixUtils.selectRowsAndColumns(data,
|
|
MatrixUtils.range(1, data.length-1), new int[] {0});
|
|
double[][] others = MatrixUtils.selectRowsAndColumns(data,
|
|
MatrixUtils.range(0, data.length-2), new int[] {1,2,3});
|
|
|
|
ConditionalMutualInfoCalculatorMultiVariateGaussian condMiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
condMiCalc.initialise(1, 1, 3);
|
|
condMiCalc.setObservations(source, dest, others);
|
|
|
|
// Now check that the Cholesky decomposition matches that for the
|
|
// expected covariance matrix:
|
|
// (Note that this was computed from all available observations; here
|
|
// we're cutting off some of the first and last observations where there is
|
|
// no matching pair in the source/dest, so results will differ slightly)
|
|
double[][] expectedCov = new double[][]
|
|
{{0.9647348336238838, 3.206553219847798E-5, -0.0013932612411635703, 0.04178350449818639, -0.01494202491454874},
|
|
{3.206553219847798E-5, 0.9647348336238838, -0.055547119949140286, -0.0020067804899770256, 0.02693742557840663},
|
|
{-0.0013932612411635703, -0.055547119949140286, 1.0800072991165575, -0.009974731537464664, -2.1485745647111378E-4},
|
|
{0.04178350449818639, -0.0020067804899770256, -0.009974731537464664, 0.48319024794457854, -0.011333013565018278},
|
|
{-0.01494202491454874, 0.02693742557840663, -2.1485745647111378E-4, -0.011333013565018278, 0.5018806693655076}};
|
|
double[][] expectedCholesky = MatrixUtils.CholeskyDecomposition(expectedCov);
|
|
|
|
for (int r = 0; r < expectedCholesky.length; r++) {
|
|
for (int c = 0; c < expectedCholesky[r].length; c++) {
|
|
// As above, results will differ slightly., so allow larger than
|
|
// usual margin for error (plus amplification then occurs in
|
|
// computing the Cholesky decomposition):
|
|
assertEquals(expectedCholesky[r][c], condMiCalc.L[r][c], 0.001);
|
|
}
|
|
}
|
|
|
|
//============================
|
|
// Case 2: Covariance conditioned on other variables:
|
|
// The joint covariance matrix here is just what it would be if we were
|
|
// measuring the complete transfer entropy
|
|
|
|
source = MatrixUtils.selectRowsAndColumns(data,
|
|
MatrixUtils.range(0, data.length-2), new int[] {1});
|
|
dest = MatrixUtils.selectRowsAndColumns(data,
|
|
MatrixUtils.range(1, data.length-1), new int[] {0});
|
|
others = MatrixUtils.selectRowsAndColumns(data,
|
|
MatrixUtils.range(0, data.length-2), new int[] {0,2,3});
|
|
|
|
condMiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
condMiCalc.initialise(1, 1, 3);
|
|
condMiCalc.setObservations(source, dest, others);
|
|
|
|
// Now check that the Cholesky decomposition matches that for the
|
|
// expected covariance matrix:
|
|
// (Note that this was computed from all available observations; here
|
|
// we're cutting off some of the first and last observations where there is
|
|
// no matching pair in the source/dest, so results will differ slightly)
|
|
expectedCov = new double[][]
|
|
{{1.0800072991165575, -0.055547119949140286, -0.0013932612411635703, -0.009974731537464664, -2.1485745647111378E-4},
|
|
{-0.055547119949140286, 0.9647348336238838, 3.206553219847798E-5, -0.0020067804899770256, 0.02693742557840663},
|
|
{-0.0013932612411635703, 3.206553219847798E-5, 0.9647348336238838, 0.04178350449818639, -0.01494202491454874},
|
|
{-0.009974731537464664, -0.0020067804899770256, 0.04178350449818639, 0.48319024794457854, -0.011333013565018278},
|
|
{-2.1485745647111378E-4, 0.02693742557840663, -0.01494202491454874, -0.011333013565018278, 0.5018806693655076}};
|
|
expectedCholesky = MatrixUtils.CholeskyDecomposition(expectedCov);
|
|
|
|
for (int r = 0; r < expectedCholesky.length; r++) {
|
|
for (int c = 0; c < expectedCholesky[r].length; c++) {
|
|
// As above, results will differ slightly., so allow larger than
|
|
// usual margin for error (plus amplification then occurs in
|
|
// computing the Cholesky decomposition):
|
|
assertEquals(expectedCholesky[r][c], condMiCalc.L[r][c], 0.001);
|
|
}
|
|
}
|
|
|
|
// For future reference, the covariance matrix for TE with k=2 on this
|
|
// example (same source and dest) should be (verified with octave):
|
|
/* expectedCov = new double[][]
|
|
{{1.0800072991165575, -0.055547119949140286, -0.0013932612411635703, -0.020520351423877373, -0.009974731537464664, -2.1485745647111378E-4},
|
|
{-0.055547119949140286, 0.9647348336238838, 3.206553219847798E-5, 0.05415562847372847, -0.0020067804899770256, 0.02693742557840663},
|
|
{-0.0013932612411635703, 3.206553219847798E-5, 0.9647348336238838, 3.206553219847798E-5, 0.04178350449818639, -0.01494202491454874},
|
|
{-0.020520351423877373, 0.05415562847372847, 3.206553219847798E-5, 0.9647348336238838, 0.350905073828977, -0.013825394184539444},
|
|
{-0.009974731537464664, -0.0020067804899770256, 0.04178350449818639, 0.350905073828977, 0.48319024794457854, -0.011333013565018278},
|
|
{-2.1485745647111378E-4, 0.02693742557840663, -0.01494202491454874, -0.013825394184539444, -0.011333013565018278, 0.5018806693655076}};
|
|
*/
|
|
|
|
}
|
|
|
|
/**
|
|
* Test whether, if the conditional variable has zero covariance,
|
|
* that the method just returns the MI
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public void testZeroCovarianceConditional() throws Exception {
|
|
ConditionalMutualInfoCalculatorMultiVariateGaussian condMiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
condMiCalc.initialise(1, 1, 1);
|
|
double covar1 = 1;
|
|
double covar2 = 0.8;
|
|
double crossCovar = 0.6;
|
|
double[][] covariance = new double[][] {
|
|
{covar1, crossCovar, 0.0},
|
|
{crossCovar, covar2, 0.0},
|
|
{0.0, 0.0, 0.0}};
|
|
condMiCalc.setCovariance(covariance, false);
|
|
double condMi = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(0.5 * Math.log(covar1 * covar2 / (covar1 * covar2 - crossCovar*crossCovar)),
|
|
condMi, 0.0000000001);
|
|
}
|
|
|
|
public void testNoConditional() throws Exception {
|
|
ArrayFileReader afr = new ArrayFileReader("demos/data/4ColsPairedOneStepNoisyDependence-1.txt");
|
|
double[][] data = afr.getDouble2DMatrix();
|
|
double[] source = MatrixUtils.selectColumn(data, 1);
|
|
double[] dest = MatrixUtils.selectColumn(data, 2);
|
|
|
|
// Set up the value we expect from MI:
|
|
MutualInfoCalculatorMultiVariateGaussian miCalc =
|
|
new MutualInfoCalculatorMultiVariateGaussian();
|
|
miCalc.initialise(1, 1);
|
|
miCalc.setObservations(source, dest);
|
|
double mi = miCalc.computeAverageLocalOfObservations();
|
|
|
|
// Now compute via CMI calculator with null passed:
|
|
ConditionalMutualInfoCalculatorMultiVariateGaussian condMiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
condMiCalc.initialise(1, 1, 0);
|
|
condMiCalc.setObservations(source, dest, null);
|
|
double condMi = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(mi, condMi, 0.000001);
|
|
|
|
// Now compute via CMI calculator with dummy column passed:
|
|
condMiCalc.initialise(1, 1, 0);
|
|
condMiCalc.setObservations(source, dest, MatrixUtils.selectColumn(data, 3));
|
|
condMi = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(mi, condMi, 0.000001);
|
|
|
|
// Now compute via CMI calculator with empty column passed (all as 2D):
|
|
condMiCalc.initialise(1, 1, 0);
|
|
condMiCalc.setObservations(MatrixUtils.selectColumns(data, 1, 1), MatrixUtils.selectColumns(data, 2, 1), new double[1000][0]);
|
|
condMi = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(mi, condMi, 0.000001);
|
|
}
|
|
|
|
public void testBiasCorrectionDoesNotChangeAnalyticPValue() throws Exception {
|
|
ConditionalMutualInfoCalculatorMultiVariateGaussian cmiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
|
|
int dimensions = 2;
|
|
int timeSteps = 100;
|
|
// generate some random data
|
|
RandomGenerator rg = new RandomGenerator();
|
|
double[][] sourceData = rg.generateNormalData(timeSteps, dimensions,
|
|
0, 1);
|
|
double[][] destData = rg.generateNormalData(timeSteps, dimensions,
|
|
0, 1);
|
|
double[][] condData = rg.generateNormalData(timeSteps, dimensions,
|
|
0, 1);
|
|
|
|
cmiCalc.setProperty(MutualInfoCalculatorMultiVariateGaussian.PROP_BIAS_CORRECTION, "false");
|
|
cmiCalc.initialise(dimensions, dimensions, dimensions);
|
|
cmiCalc.setObservations(sourceData, destData, condData);
|
|
double avNotBiasCorrected = cmiCalc.computeAverageLocalOfObservations();
|
|
ChiSquareMeasurementDistribution distroNotBiasCorrected = cmiCalc.computeSignificance();
|
|
assertEquals(avNotBiasCorrected, distroNotBiasCorrected.actualValue, 0.0000001);
|
|
|
|
// Now run again with bias correction:
|
|
cmiCalc.setProperty(MutualInfoCalculatorMultiVariateGaussian.PROP_BIAS_CORRECTION, "true");
|
|
cmiCalc.initialise(dimensions, dimensions, dimensions);
|
|
cmiCalc.setObservations(sourceData, destData, condData);
|
|
double avBiasCorrected = cmiCalc.computeAverageLocalOfObservations();
|
|
ChiSquareMeasurementDistribution distroBiasCorrected = cmiCalc.computeSignificance();
|
|
assertEquals(avBiasCorrected, distroBiasCorrected.actualValue, 0.0000001);
|
|
// And now check that the pValues are unchanged whether we bias correct or not:
|
|
assertEquals(distroNotBiasCorrected.pValue, distroBiasCorrected.pValue, 0.0000001);
|
|
}
|
|
|
|
protected int timeStepsDepCheck = 100;
|
|
|
|
/**
|
|
* Check that the tests of linear dependencies that we also use for the MI calculator
|
|
* apply for conditional MI when the conditional is empty in some fashion
|
|
* (i.e. no dimensions, null or all constants)
|
|
*
|
|
*/
|
|
public void testHandlingLinearDependenciesNoConditional() throws Exception {
|
|
// 2D array with 0 variables (columns):
|
|
checkHandlingLinearDependenciesUnrelatedOrNoConditional(new double[timeStepsDepCheck][0], 0);
|
|
|
|
// Array of constants for each variable
|
|
for (int c = 1; c < 5; c++) {
|
|
double[][] conditional = new double[timeStepsDepCheck][c];
|
|
for (int j = 0; j < c; j++) {
|
|
MatrixUtils.copyIntoColumn(conditional, j,
|
|
MatrixUtils.constantArray(timeStepsDepCheck, j)); // Just use a constant, not necessraily zeros
|
|
}
|
|
checkHandlingLinearDependenciesUnrelatedOrNoConditional(conditional, c);
|
|
}
|
|
// Null conditional
|
|
checkHandlingLinearDependenciesUnrelatedOrNoConditional(null, 0);
|
|
}
|
|
|
|
/**
|
|
* Check that the tests of linear dependencies that we also use for the MI calculator
|
|
* apply for conditional MI when the conditional is irrelevant to source and dest
|
|
*
|
|
*/
|
|
public void testHandlingLinearDependenciesIrrelevantConditional() throws Exception {
|
|
RandomGenerator rg = new RandomGenerator();
|
|
for (int c = 0; c < 5; c++) {
|
|
// Try with just noisy conditionals:
|
|
double[][] condData = rg.generateNormalData(timeStepsDepCheck, c,
|
|
0, 1);
|
|
checkHandlingLinearDependenciesUnrelatedOrNoConditional(condData, c);
|
|
// Try also if one of the variables is redundant with others
|
|
if (c > 2) {
|
|
condData = rg.generateNormalData(timeStepsDepCheck, c, 0, 1);
|
|
MatrixUtils.copyIntoColumn(condData, 2,
|
|
MatrixUtils.add(
|
|
MatrixUtils.selectColumn(condData, 0),
|
|
MatrixUtils.selectColumn(condData, 1)));
|
|
checkHandlingLinearDependenciesUnrelatedOrNoConditional(condData, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that the tests of linear dependencies that we also use for the MI calculator
|
|
* apply for conditional MI when the conditional is empty in some fashion or irrelevant
|
|
*
|
|
* @param emptyConditional
|
|
* @param conditionalDims
|
|
* @throws Exception
|
|
*/
|
|
protected void checkHandlingLinearDependenciesUnrelatedOrNoConditional(
|
|
double[][] emptyConditional, int conditionalDims) throws Exception {
|
|
|
|
ConditionalMutualInfoCalculatorMultiVariateGaussian condMiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
|
|
int dimensions = 2; // Assumed to be >-= 2
|
|
assertTrue(dimensions == 2);
|
|
RandomGenerator rg = new RandomGenerator();
|
|
|
|
// Generate some random data and do an MI on copied version
|
|
// - both dimensions are copied
|
|
double[][] sourceData = rg.generateNormalData(timeStepsDepCheck, dimensions,
|
|
0, 1);
|
|
double[][] destData = MatrixUtils.arrayCopy(sourceData);
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, emptyConditional);
|
|
double miCopied = condMiCalc.computeAverageLocalOfObservations();
|
|
assertTrue(Double.isInfinite(miCopied));
|
|
|
|
// Now overwrite one of the columns, and check result is still infinite
|
|
// with two columns across the variables the same (only one dimension copied):
|
|
MatrixUtils.copyIntoColumn(sourceData, 1,
|
|
rg.generateNormalData(timeStepsDepCheck, 0, 1));
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, emptyConditional);
|
|
double miOneCopied = condMiCalc.computeAverageLocalOfObservations();
|
|
assertTrue(Double.isInfinite(miOneCopied));
|
|
|
|
// Now make the source columns a copy of themselves, and check that it can ignore this
|
|
double[] indpColumn = MatrixUtils.selectColumn(sourceData, 1);
|
|
MatrixUtils.copyIntoColumn(sourceData, 0,
|
|
indpColumn);
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, emptyConditional);
|
|
double mi1SourceFrom2 = condMiCalc.computeAverageLocalOfObservations();
|
|
assertTrue(Double.isFinite(mi1SourceFrom2));
|
|
condMiCalc.initialise(1,dimensions, conditionalDims);
|
|
// Should be the same whether we compute this from both source columns or only 1
|
|
double[][] source1Column = new double[timeStepsDepCheck][1];
|
|
MatrixUtils.copyIntoColumn(source1Column, 0, indpColumn);
|
|
condMiCalc.setObservations(source1Column, destData, emptyConditional);
|
|
double mi1DSourceFrom1 = condMiCalc.computeAverageLocalOfObservations();
|
|
assertTrue(Double.isFinite(mi1DSourceFrom1));
|
|
assertEquals(mi1DSourceFrom1, mi1SourceFrom2, 0.00000001);
|
|
// And check it works if we flip source and dest:
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(destData, sourceData, emptyConditional);
|
|
double mi1SourceFrom2Flipped = condMiCalc.computeAverageLocalOfObservations();
|
|
assertTrue(Double.isFinite(mi1SourceFrom2Flipped));
|
|
assertEquals(mi1DSourceFrom1, mi1SourceFrom2Flipped, 0.00000001);
|
|
|
|
// Refresh data, with a third dependent source variable in and check that this doesn't change things:
|
|
sourceData = rg.generateNormalData(timeStepsDepCheck, dimensions,
|
|
0, 1);
|
|
destData = rg.generateNormalData(timeStepsDepCheck, dimensions + 1,
|
|
0, 1);
|
|
MatrixUtils.copyIntoColumn(destData, dimensions,
|
|
MatrixUtils.add(MatrixUtils.selectColumn(destData, 0), MatrixUtils.selectColumn(destData, 1)));
|
|
condMiCalc.initialise(dimensions, dimensions + 1, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, emptyConditional);
|
|
double mi1RedundantDest = condMiCalc.computeAverageLocalOfObservations();
|
|
assertTrue(Double.isFinite(mi1RedundantDest));
|
|
// check that it works flipped
|
|
condMiCalc.initialise(dimensions+1, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(destData, sourceData, emptyConditional);
|
|
double mi1Redundantsource = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(mi1RedundantDest, mi1Redundantsource, 1e-7);
|
|
// Check that it's the same if we only use the independent variables:
|
|
destData = MatrixUtils.selectColumns(destData, new int[] {0, 1});
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, emptyConditional);
|
|
double miDestWithoutRedundant = condMiCalc.computeAverageLocalOfObservations();
|
|
assertTrue(Double.isFinite(miDestWithoutRedundant));
|
|
assertEquals(mi1RedundantDest, miDestWithoutRedundant, 0.00000001);
|
|
|
|
// First check that the MI is not precisely zero if only one of the sub-variables is a zero:
|
|
MatrixUtils.copyIntoColumn(destData, 0,
|
|
MatrixUtils.constantArray(timeStepsDepCheck, 0));
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, emptyConditional);
|
|
double miOneColZero = condMiCalc.computeAverageLocalOfObservations();
|
|
// Check that the MI here is not precisely zero, it shouldn't be for a finite sample length
|
|
// System.out.println(miOneColZero);
|
|
assertTrue(Math.abs(miOneColZero) > 1e-12);
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(destData, sourceData, emptyConditional); // check if we swap the variables all is the same
|
|
double miOneColZeroSwapped = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(miOneColZero, miOneColZeroSwapped, 1e-7);
|
|
// Now do the same to a sub-variable of the source:
|
|
MatrixUtils.copyIntoColumn(sourceData, 1,
|
|
MatrixUtils.constantArray(timeStepsDepCheck, 0));
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, emptyConditional);
|
|
double miOneColZeroSourceAlso = condMiCalc.computeAverageLocalOfObservations();
|
|
assertTrue(Math.abs(miOneColZeroSourceAlso) > 1e-12);
|
|
// Check that the result is the same if we only supplied the non-zero columns:
|
|
condMiCalc.initialise(1, 1, conditionalDims);
|
|
condMiCalc.setObservations(MatrixUtils.selectColumns(sourceData, new int[] {0}),
|
|
MatrixUtils.selectColumns(destData, new int[] {1}),
|
|
emptyConditional);
|
|
double miOneColZeroSourceAlsoUnivariateCalcs = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(miOneColZeroSourceAlsoUnivariateCalcs, miOneColZeroSourceAlso, 1e-7);
|
|
|
|
// Test that we get a zero if no variance within any source or target variables
|
|
MatrixUtils.copyIntoColumn(destData, 1,
|
|
MatrixUtils.constantArray(timeStepsDepCheck, 0));
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, emptyConditional);
|
|
double miDestAllZeros = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(miDestAllZeros, 0, 1e-13);
|
|
// And the other way around:
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(destData, sourceData, emptyConditional);
|
|
double miSourceAllZeros = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(miSourceAllZeros, 0, 1e-13);
|
|
|
|
// And if only a univariate is all zeros:
|
|
condMiCalc.initialise(1, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(MatrixUtils.selectColumns(destData, 1, 1),
|
|
rg.generateNormalData(timeStepsDepCheck, dimensions, 0, 1), emptyConditional);
|
|
double miSourceZero = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(miSourceZero, 0, 1e-13);
|
|
// and the other way around:
|
|
condMiCalc.initialise(dimensions, 1, conditionalDims);
|
|
condMiCalc.setObservations(rg.generateNormalData(timeStepsDepCheck, dimensions, 0, 1),
|
|
MatrixUtils.selectColumns(destData, 1, 1), emptyConditional);
|
|
double miDestZero = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(miDestZero, 0, 1e-13);
|
|
}
|
|
|
|
public void testHandlingLinearDependenciesWithConditionalRelated() throws Exception {
|
|
|
|
ConditionalMutualInfoCalculatorMultiVariateGaussian condMiCalc =
|
|
new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
|
|
|
int dimensions = 2; // Assumed to be 2
|
|
assertTrue(dimensions == 2);
|
|
int conditionalDims = 2; // Assumed to be 2
|
|
assertTrue(conditionalDims == 2);
|
|
RandomGenerator rg = new RandomGenerator();
|
|
|
|
// Generate some random data and do an MI on copied version
|
|
// - both dimensions are copied
|
|
double[][] sourceData = rg.generateNormalData(timeStepsDepCheck, dimensions,
|
|
0, 1);
|
|
double[][] condData = rg.generateNormalData(timeStepsDepCheck, conditionalDims,
|
|
0, 1);
|
|
double[][] destData = MatrixUtils.arrayCopy(condData);
|
|
|
|
// Conditional renders dest fully redundant (straight copy of conditional) -> 0
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, condData);
|
|
double miDestRedundantWithCond = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(0, miDestRedundantWithCond, 1e-7);
|
|
// Or is sum of two variables
|
|
MatrixUtils.copyIntoColumn(destData, 0,
|
|
MatrixUtils.add(MatrixUtils.selectColumn(condData, 0),
|
|
MatrixUtils.selectColumn(condData, 1)));
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, condData);
|
|
double miDestRedundantWithCondSum = condMiCalc.computeAverageLocalOfObservations();
|
|
assertEquals(0, miDestRedundantWithCondSum, 1e-7);
|
|
// And the other way around:
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(destData, sourceData, condData);
|
|
assertEquals(0, condMiCalc.computeAverageLocalOfObservations(), 1e-7);
|
|
// And what if we only use a univariate dest:
|
|
condMiCalc.initialise(dimensions, 1, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, MatrixUtils.selectColumns(destData, 0, 1), condData);
|
|
assertEquals(0, condMiCalc.computeAverageLocalOfObservations(), 1e-7);
|
|
|
|
// Conditional rendering a dest column (0) fully dependent doesn't change other result (for its column 1)
|
|
MatrixUtils.copyIntoColumn(destData, 1, rg.generateNormalData(timeStepsDepCheck, 0, 1));
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, condData);
|
|
double miFromOneIndpCol = condMiCalc.computeAverageLocalOfObservations();
|
|
condMiCalc.initialise(dimensions, 1, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, MatrixUtils.selectColumns(destData, 1, 1), condData);
|
|
assertEquals(miFromOneIndpCol, condMiCalc.computeAverageLocalOfObservations(), 1e-7);
|
|
|
|
// Conditional renders source and dest dependent (e.g. by summing the two) - inf
|
|
// Whether it's only one variable or both
|
|
destData = MatrixUtils.add(sourceData, condData);
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, destData, condData);
|
|
assertTrue(Double.isInfinite(condMiCalc.computeAverageLocalOfObservations()));
|
|
condMiCalc.initialise(dimensions, 1, conditionalDims);
|
|
condMiCalc.setObservations(sourceData, MatrixUtils.selectColumns(destData, 0, 1), condData);
|
|
assertTrue(Double.isInfinite(condMiCalc.computeAverageLocalOfObservations()));
|
|
// And now flip the source and dest
|
|
condMiCalc.initialise(dimensions, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(destData, sourceData, condData);
|
|
assertTrue(Double.isInfinite(condMiCalc.computeAverageLocalOfObservations()));
|
|
condMiCalc.initialise(1, dimensions, conditionalDims);
|
|
condMiCalc.setObservations(MatrixUtils.selectColumns(destData, 0, 1), sourceData, condData);
|
|
assertTrue(Double.isInfinite(condMiCalc.computeAverageLocalOfObservations()));
|
|
}
|
|
|
|
}
|