jidt/java/unittests/infodynamics/measures/continuous/gaussian/ConditionalMutualInfoMultiV...

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, (double[][]) 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()));
}
}