From f67e2748192bb557f2da4a7bdafa46b31946fdff Mon Sep 17 00:00:00 2001 From: Pedro Mediano Date: Sun, 24 Jan 2021 22:06:39 +0000 Subject: [PATCH] Added Gaussian implementation of various multivariate IT measures and unit tests. --- ...ualTotalCorrelationCalculatorGaussian.java | 122 ++++++++++++++ ...iVariateInfoMeasureCalculatorGaussian.java | 152 ++++++++++++++++++ .../gaussian/OInfoCalculatorGaussian.java | 134 +++++++++++++++ .../gaussian/SInfoCalculatorGaussian.java | 120 ++++++++++++++ ...alCorrelationCalculatorGaussianTester.java | 133 +++++++++++++++ .../OInfoCalculatorGaussianTester.java | 150 +++++++++++++++++ .../SInfoCalculatorGaussianTester.java | 129 +++++++++++++++ 7 files changed, 940 insertions(+) create mode 100644 java/source/infodynamics/measures/continuous/gaussian/DualTotalCorrelationCalculatorGaussian.java create mode 100644 java/source/infodynamics/measures/continuous/gaussian/MultiVariateInfoMeasureCalculatorGaussian.java create mode 100644 java/source/infodynamics/measures/continuous/gaussian/OInfoCalculatorGaussian.java create mode 100644 java/source/infodynamics/measures/continuous/gaussian/SInfoCalculatorGaussian.java create mode 100644 java/unittests/infodynamics/measures/continuous/gaussian/DualTotalCorrelationCalculatorGaussianTester.java create mode 100644 java/unittests/infodynamics/measures/continuous/gaussian/OInfoCalculatorGaussianTester.java create mode 100644 java/unittests/infodynamics/measures/continuous/gaussian/SInfoCalculatorGaussianTester.java diff --git a/java/source/infodynamics/measures/continuous/gaussian/DualTotalCorrelationCalculatorGaussian.java b/java/source/infodynamics/measures/continuous/gaussian/DualTotalCorrelationCalculatorGaussian.java new file mode 100644 index 0000000..7d3c572 --- /dev/null +++ b/java/source/infodynamics/measures/continuous/gaussian/DualTotalCorrelationCalculatorGaussian.java @@ -0,0 +1,122 @@ +/* + * Java Information Dynamics Toolkit (JIDT) + * Copyright (C) 2017, Joseph T. Lizier, Ipek Oezdemir and Pedro Mediano + * + * 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 . + */ + +package infodynamics.measures.continuous.gaussian; + +import infodynamics.utils.MatrixUtils; + +/** + *

Computes the differential dual total correlation (DTC) of a given multivariate + * double[][] set of + * observations (extending {@link MultiVariateInfoMeasureCalculatorGaussian}), + * assuming that the probability distribution function for these observations is + * a multivariate Gaussian distribution.

+ * + *

Usage is as per the paradigm outlined for {@link MultiVariateInfoMeasureCalculatorCommon}. + *

+ * + *

References:
+ *

+ * + * @author Pedro A.M. Mediano (email, + * www) + */ +public class DualTotalCorrelationCalculatorGaussian + extends MultiVariateInfoMeasureCalculatorGaussian { + + /** + * Constructor. + */ + public DualTotalCorrelationCalculatorGaussian() { + // Nothing to do + } + + /** + * {@inheritDoc} + * + * @return the average DTC in nats (not bits!) + * @throws Exception if not sufficient data have been provided, or if the + * supplied covariance matrix is invalid. + */ + public double computeAverageLocalOfObservations() throws Exception { + + if (covariance == null) { + throw new Exception("Cannot calculate DTC without having " + + "a covariance either supplied or computed via setObservations()"); + } + + if (!isComputed) { + double dtc = - (dimensions - 1)*Math.log(MatrixUtils.determinantSymmPosDefMatrix(covariance)); + for (int i = 0; i < dimensions; i++) { + int[] idx = allExcept(i, dimensions); + double[][] marginal_cov = MatrixUtils.selectRowsAndColumns(covariance, idx, idx); + dtc += Math.log(MatrixUtils.determinantSymmPosDefMatrix(marginal_cov)); + } + // This "0.5" comes from the entropy formula for Gaussians: h = 0.5*logdet(2*pi*e*Sigma) + lastAverage = 0.5*dtc;; + isComputed = true; + } + + return lastAverage; + } + + /** + * {@inheritDoc} + * + * @return the "time-series" of local DTC values in nats (not bits!) + * for the supplied states. + * @throws Exception if not sufficient data have been provided, or if the + * supplied covariance matrix is invalid. + */ + public double[] computeLocalUsingPreviousObservations(double[][] states) throws Exception { + + if ((means == null) || (covariance == null)) { + throw new Exception("Cannot compute local values without having means " + + "and covariance either supplied or computed via setObservations()"); + } + + EntropyCalculatorMultiVariateGaussian hCalc = new EntropyCalculatorMultiVariateGaussian(); + hCalc.initialise(dimensions); + hCalc.setCovarianceAndMeans(covariance, means); + double[] localValues = MatrixUtils.multiply(hCalc.computeLocalUsingPreviousObservations(states), -(dimensions - 1)); + + for (int i = 0; i < dimensions; i++) { + int[] idx = allExcept(i, dimensions); + double[][] marginal_cov = MatrixUtils.selectRowsAndColumns(covariance, idx, idx); + double[] marginal_means = MatrixUtils.select(means, idx); + double[][] marginal_state = MatrixUtils.selectColumns(states, idx); + + hCalc.initialise(dimensions - 1); + hCalc.setCovarianceAndMeans(marginal_cov, marginal_means); + double[] thisLocals = hCalc.computeLocalUsingPreviousObservations(marginal_state); + + localValues = MatrixUtils.add(localValues, thisLocals); + + } + + return localValues; + + } + +} + diff --git a/java/source/infodynamics/measures/continuous/gaussian/MultiVariateInfoMeasureCalculatorGaussian.java b/java/source/infodynamics/measures/continuous/gaussian/MultiVariateInfoMeasureCalculatorGaussian.java new file mode 100644 index 0000000..f8d0933 --- /dev/null +++ b/java/source/infodynamics/measures/continuous/gaussian/MultiVariateInfoMeasureCalculatorGaussian.java @@ -0,0 +1,152 @@ +/* + * Java Information Dynamics Toolkit (JIDT) + * Copyright (C) 2017, Joseph T. Lizier, Ipek Oezdemir and Pedro Mediano + * + * 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 . + */ + +package infodynamics.measures.continuous.gaussian; + +import infodynamics.measures.continuous.MultiVariateInfoMeasureCalculatorCommon; +import infodynamics.utils.MatrixUtils; + +/** + *

Base class with common functionality for child class implementations of + * multivariate information measures on a given multivariate + * double[][] set of + * observations (extending {@link MultiVariateInfoMeasureCalculatorCommon}), + * assuming that the probability distribution function for these observations is + * a multivariate Gaussian distribution.

+ * + *

Usage is as per the paradigm outlined for {@link MultiVariateInfoMeasureCalculatorCommon}, + * with: + *

    + *
  • For constructors see the child classes.
  • + *
  • Further properties are defined in {@link #setProperty(String, String)}.
  • + *
  • Computed values are in nats, not bits!
  • + *
+ *

+ * + *

References:
+ *

+ * + * @author Pedro A.M. Mediano (email, + * www) + */ +public abstract class MultiVariateInfoMeasureCalculatorGaussian + extends MultiVariateInfoMeasureCalculatorCommon { + + /** + * Covariance of the system. Can be calculated from supplied observations + * or supplied directly by the user. + */ + double[][] covariance = null; + + /** + * Means of the system. Can be calculated from supplied observations + * or supplied directly by the user. + */ + double[] means = null; + + /** + * Whether the current covariance matrix has been determined from data or + * supplied directly. This changes the approach to local measures and + * significance testing. + */ + boolean covFromObservations; + + @Override + public void finaliseAddObservations() throws Exception { + super.finaliseAddObservations(); + + this.means = MatrixUtils.means(observations); + setCovariance(MatrixUtils.covarianceMatrix(observations), true); + + return; + } + + /** + *

Set the covariance of the distribution for which we will compute the + * measure directly, without supplying observations.

+ * + *

See {@link #setCovariance(double[][], boolean)}. + * + * @param covariance covariance matrix of the system + * @throws Exception for covariance matrix not matching the expected dimensions, + * being non-square, asymmetric or non-positive definite + */ + public void setCovariance(double[][] covariance) throws Exception { + setCovariance(covariance, false); + } + + /** + *

Set the covariance of the distribution for which we will compute the + * measure.

+ * + *

This is an alternative to sequences of calls to {@link #setObservations(double[][])} or + * {@link #addObservations(double[][])} etc. + * Note that without setting any observations, you cannot later + * call {@link #computeLocalOfPreviousObservations()}.

+ * + * @param covariance covariance matrix of the system + * @param means mean of the system + */ + public void setCovarianceAndMeans(double[][] covariance, double[] means) + throws Exception { + this.means = means; + setCovariance(covariance, false); + } + + /** + *

Set the covariance of the distribution for which we will compute the + * measure.

+ * + *

This is an alternative to sequences of calls to {@link #setObservations(double[][])} or + * {@link #addObservations(double[][])} etc. + * Note that without setting any observations, you cannot later + * call {@link #computeLocalOfPreviousObservations()}, and without + * providing the means of the variables, you cannot later call + * {@link #computeLocalUsingPreviousObservations(double[][])}.

+ * + * @param covariance covariance matrix of the system + * @param covFromObservations whether the covariance matrix + * was determined internally from observations or not + * @throws Exception for covariance matrix not matching the expected dimensions, + * being non-square, asymmetric or non-positive definite + */ + public void setCovariance(double[][] cov, boolean covFromObservations) throws Exception { + + if (!covFromObservations) { + // Make sure we're not keeping any observations + observations = null; + } + if (cov.length != dimensions) { + throw new Exception("Supplied covariance matrix does not match initialised number of dimensions"); + } + if (cov.length != cov[0].length) { + throw new Exception("Covariance matrices must be square"); + } + + this.covFromObservations = covFromObservations; + this.covariance = cov; + + } + +} + diff --git a/java/source/infodynamics/measures/continuous/gaussian/OInfoCalculatorGaussian.java b/java/source/infodynamics/measures/continuous/gaussian/OInfoCalculatorGaussian.java new file mode 100644 index 0000000..83c75dc --- /dev/null +++ b/java/source/infodynamics/measures/continuous/gaussian/OInfoCalculatorGaussian.java @@ -0,0 +1,134 @@ +/* + * Java Information Dynamics Toolkit (JIDT) + * Copyright (C) 2017, Joseph T. Lizier, Ipek Oezdemir and Pedro Mediano + * + * 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 . + */ + +package infodynamics.measures.continuous.gaussian; + +import infodynamics.utils.MatrixUtils; + +/** + *

Computes the differential O-information of a given multivariate + * double[][] set of + * observations (extending {@link MultiVariateInfoMeasureCalculatorGaussian}), + * assuming that the probability distribution function for these observations is + * a multivariate Gaussian distribution.

+ * + *

Usage is as per the paradigm outlined for {@link MultiVariateInfoMeasureCalculatorCommon}. + *

+ * + *

References:
+ *

+ * + * @author Pedro A.M. Mediano (email, + * www) + */ +public class OInfoCalculatorGaussian + extends MultiVariateInfoMeasureCalculatorGaussian { + + /** + * Constructor. + */ + public OInfoCalculatorGaussian() { + // Nothing to do + } + + /** + * {@inheritDoc} + * + * @return the average O-info in nats (not bits!) + * @throws Exception if not sufficient data have been provided, or if the + * supplied covariance matrix is invalid. + */ + public double computeAverageLocalOfObservations() throws Exception { + + if (covariance == null) { + throw new Exception("Cannot calculate O-Info without having " + + "a covariance either supplied or computed via setObservations()"); + } + + if (!isComputed) { + double oinfo = (dimensions - 2)*Math.log(MatrixUtils.determinantSymmPosDefMatrix(covariance)); + for (int i = 0; i < dimensions; i++) { + int[] idx = allExcept(i, dimensions); + double[][] marginal_cov = MatrixUtils.selectRowsAndColumns(covariance, idx, idx); + oinfo += Math.log(covariance[i][i]) - Math.log(MatrixUtils.determinantSymmPosDefMatrix(marginal_cov)); + } + // This "0.5" comes from the entropy formula for Gaussians: h = 0.5*logdet(2*pi*e*Sigma) + lastAverage = 0.5*oinfo;; + isComputed = true; + } + + return lastAverage; + } + + /** + * {@inheritDoc} + * + * @return the "time-series" of local O-info values in nats (not bits!) + * for the supplied states. + * @throws Exception if not sufficient data have been provided, or if the + * supplied covariance matrix is invalid. + */ + public double[] computeLocalUsingPreviousObservations(double[][] states) throws Exception { + + if ((means == null) || (covariance == null)) { + throw new Exception("Cannot compute local values without having means " + + "and covariance either supplied or computed via setObservations()"); + } + + EntropyCalculatorMultiVariateGaussian hCalc = new EntropyCalculatorMultiVariateGaussian(); + hCalc.initialise(dimensions); + hCalc.setCovarianceAndMeans(covariance, means); + double[] localValues = MatrixUtils.multiply(hCalc.computeLocalUsingPreviousObservations(states), dimensions - 2); + + for (int i = 0; i < dimensions; i++) { + int[] idx = allExcept(i, dimensions); + + // Local entropy of this variable (i) only + double[][] this_cov = MatrixUtils.selectRowsAndColumns(covariance, i, 1, i, 1); + double[] this_means = MatrixUtils.select(means, i, 1); + double[][] this_state = MatrixUtils.selectColumns(states, i, 1); + + hCalc.initialise(1); + hCalc.setCovarianceAndMeans(this_cov, this_means); + double[] thisLocals = hCalc.computeLocalUsingPreviousObservations(this_state); + + // Local entropy of the rest of the variables (0, ... i-1, i+1, ... D) + double[][] rest_cov = MatrixUtils.selectRowsAndColumns(covariance, idx, idx); + double[] rest_means = MatrixUtils.select(means, idx); + double[][] rest_state = MatrixUtils.selectColumns(states, idx); + + hCalc.initialise(dimensions - 1); + hCalc.setCovarianceAndMeans(rest_cov, rest_means); + double[] restLocals = hCalc.computeLocalUsingPreviousObservations(rest_state); + + localValues = MatrixUtils.add(localValues, MatrixUtils.subtract(thisLocals, restLocals)); + + } + + return localValues; + + } + +} + + diff --git a/java/source/infodynamics/measures/continuous/gaussian/SInfoCalculatorGaussian.java b/java/source/infodynamics/measures/continuous/gaussian/SInfoCalculatorGaussian.java new file mode 100644 index 0000000..02c5627 --- /dev/null +++ b/java/source/infodynamics/measures/continuous/gaussian/SInfoCalculatorGaussian.java @@ -0,0 +1,120 @@ +/* + * Java Information Dynamics Toolkit (JIDT) + * Copyright (C) 2017, Joseph T. Lizier, Ipek Oezdemir and Pedro Mediano + * + * 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 . + */ + +package infodynamics.measures.continuous.gaussian; + +import infodynamics.utils.MatrixUtils; + +/** + *

Computes the differential S-information of a given multivariate + * double[][] set of + * observations (extending {@link MultiVariateInfoMeasureCalculatorGaussian}), + * assuming that the probability distribution function for these observations is + * a multivariate Gaussian distribution.

+ * + *

Usage is as per the paradigm outlined for {@link MultiVariateInfoMeasureCalculatorCommon}. + *

+ * + *

References:
+ *

+ * + * @author Pedro A.M. Mediano (email, + * www) + */ +public class SInfoCalculatorGaussian + extends MultiVariateInfoMeasureCalculatorGaussian { + + /** + * Constructor. + */ + public SInfoCalculatorGaussian() { + // Nothing to do + } + + /** + * {@inheritDoc} + * + * @return the average S-info in nats (not bits!) + * @throws Exception if not sufficient data have been provided, or if the + * supplied covariance matrix is invalid. + */ + public double computeAverageLocalOfObservations() throws Exception { + + if (covariance == null) { + throw new Exception("Cannot calculate O-Info without having " + + "a covariance either supplied or computed via setObservations()"); + } + + if (!isComputed) { + + MultiInfoCalculatorGaussian tcCalc = new MultiInfoCalculatorGaussian(); + tcCalc.initialise(dimensions); + tcCalc.setCovariance(covariance); + double tc = tcCalc.computeAverageLocalOfObservations(); + + DualTotalCorrelationCalculatorGaussian dtcCalc = new DualTotalCorrelationCalculatorGaussian(); + dtcCalc.initialise(dimensions); + dtcCalc.setCovariance(covariance); + double dtc = dtcCalc.computeAverageLocalOfObservations(); + + lastAverage = tc + dtc; + isComputed = true; + } + + return lastAverage; + } + + /** + * {@inheritDoc} + * + * @return the "time-series" of local S-info values in nats (not bits!) + * for the supplied states. + * @throws Exception if not sufficient data have been provided, or if the + * supplied covariance matrix is invalid. + */ + public double[] computeLocalUsingPreviousObservations(double[][] states) throws Exception { + + if ((means == null) || (covariance == null)) { + throw new Exception("Cannot compute local values without having means " + + "and covariance either supplied or computed via setObservations()"); + } + + MultiInfoCalculatorGaussian tcCalc = new MultiInfoCalculatorGaussian(); + tcCalc.initialise(dimensions); + tcCalc.setCovarianceAndMeans(covariance, means); + double[] localTC = tcCalc.computeLocalUsingPreviousObservations(states); + + DualTotalCorrelationCalculatorGaussian dtcCalc = new DualTotalCorrelationCalculatorGaussian(); + dtcCalc.initialise(dimensions); + dtcCalc.setCovarianceAndMeans(covariance, means); + double[] localDTC = dtcCalc.computeLocalUsingPreviousObservations(states); + + double[] localValues = MatrixUtils.add(localTC, localDTC); + + return localValues; + + } + +} + + diff --git a/java/unittests/infodynamics/measures/continuous/gaussian/DualTotalCorrelationCalculatorGaussianTester.java b/java/unittests/infodynamics/measures/continuous/gaussian/DualTotalCorrelationCalculatorGaussianTester.java new file mode 100644 index 0000000..a5cd702 --- /dev/null +++ b/java/unittests/infodynamics/measures/continuous/gaussian/DualTotalCorrelationCalculatorGaussianTester.java @@ -0,0 +1,133 @@ +/* + * 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 . + */ + +package infodynamics.measures.continuous.gaussian; + +import infodynamics.utils.MatrixUtils; +import infodynamics.utils.RandomGenerator; +import junit.framework.TestCase; + +public class DualTotalCorrelationCalculatorGaussianTester extends TestCase { + + /** + * For two variables, DTC is equal to mutual information. + */ + public void testTwoVariables() throws Exception { + double[][] cov = new double[][] {{1, 0.5}, {0.5, 1}}; + + DualTotalCorrelationCalculatorGaussian dtcCalc = new DualTotalCorrelationCalculatorGaussian(); + dtcCalc.initialise(2); + dtcCalc.setCovariance(cov); + double dtc = dtcCalc.computeAverageLocalOfObservations(); + + MutualInfoCalculatorMultiVariateGaussian miCalc = new MutualInfoCalculatorMultiVariateGaussian(); + miCalc.initialise(1,1); + miCalc.setCovariance(cov, 1); + double mi = miCalc.computeAverageLocalOfObservations(); + + assertEquals(dtc, mi, 1e-6); + } + + /** + * Compare against the direct calculation of DTC as a sum of entropies using the + * entropy calculator. + */ + public void testCompareWithEntropy() throws Exception { + double[][] cov = new double[][] {{1, 0.4, 0.3}, {0.4, 1, 0.2}, {0.3, 0.2, 1}}; + + DualTotalCorrelationCalculatorGaussian dtcCalc = new DualTotalCorrelationCalculatorGaussian(); + dtcCalc.initialise(3); + dtcCalc.setCovariance(cov); + double dtc = dtcCalc.computeAverageLocalOfObservations(); + + // Calculate using an entropy calculator and picking submatrices manually + EntropyCalculatorMultiVariateGaussian hCalc = new EntropyCalculatorMultiVariateGaussian(); + hCalc.initialise(3); + hCalc.setCovariance(cov); + double dtc_hCalc = -2 * hCalc.computeAverageLocalOfObservations(); + + hCalc.initialise(2); + hCalc.setCovariance(MatrixUtils.selectRowsAndColumns(cov, new int[] {0,1}, new int[] {0,1})); + dtc_hCalc += hCalc.computeAverageLocalOfObservations(); + hCalc.initialise(2); + hCalc.setCovariance(MatrixUtils.selectRowsAndColumns(cov, new int[] {0,2}, new int[] {0,2})); + dtc_hCalc += hCalc.computeAverageLocalOfObservations(); + hCalc.initialise(2); + hCalc.setCovariance(MatrixUtils.selectRowsAndColumns(cov, new int[] {1,2}, new int[] {1,2})); + dtc_hCalc += hCalc.computeAverageLocalOfObservations(); + + assertEquals(dtc, dtc_hCalc, 1e-6); + + } + + /** + * Confirm that the local values average correctly back to the average value + */ + public void testLocalsAverageCorrectly() throws Exception { + + int dimensions = 4; + int timeSteps = 1000; + DualTotalCorrelationCalculatorGaussian dtcCalc = new DualTotalCorrelationCalculatorGaussian(); + dtcCalc.initialise(dimensions); + + // generate some random data + RandomGenerator rg = new RandomGenerator(); + double[][] data = rg.generateNormalData(timeSteps, dimensions, + 0, 1); + + dtcCalc.setObservations(data); + + double dtc = dtcCalc.computeAverageLocalOfObservations(); + double[] dtcLocal = dtcCalc.computeLocalOfPreviousObservations(); + + System.out.printf("Average was %.5f\n", dtc); + + assertEquals(dtc, MatrixUtils.mean(dtcLocal), 0.00001); + } + + /** + * Confirm that for 2D the local values equal the local MI values + * + */ + public void testLocalsEqualMI() throws Exception { + + int dimensions = 2; + int timeSteps = 100; + DualTotalCorrelationCalculatorGaussian dtcCalc = new DualTotalCorrelationCalculatorGaussian(); + dtcCalc.initialise(dimensions); + + // generate some random data + RandomGenerator rg = new RandomGenerator(); + double[][] data = rg.generateNormalData(timeSteps, dimensions, + 0, 1); + + dtcCalc.setObservations(data); + double[] dtcLocal = dtcCalc.computeLocalOfPreviousObservations(); + + MutualInfoCalculatorMultiVariateGaussian miCalc = new MutualInfoCalculatorMultiVariateGaussian(); + miCalc.initialise(1, 1); + miCalc.setObservations(MatrixUtils.selectColumn(data, 0), MatrixUtils.selectColumn(data, 1)); + double[] miLocal = miCalc.computeLocalOfPreviousObservations(); + + for (int t = 0; t < timeSteps; t++) { + assertEquals(dtcLocal[t], miLocal[t], 0.00001); + } + } + +} + diff --git a/java/unittests/infodynamics/measures/continuous/gaussian/OInfoCalculatorGaussianTester.java b/java/unittests/infodynamics/measures/continuous/gaussian/OInfoCalculatorGaussianTester.java new file mode 100644 index 0000000..15e3c46 --- /dev/null +++ b/java/unittests/infodynamics/measures/continuous/gaussian/OInfoCalculatorGaussianTester.java @@ -0,0 +1,150 @@ +/* + * 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 . + */ + +package infodynamics.measures.continuous.gaussian; + +import infodynamics.utils.MatrixUtils; +import infodynamics.utils.RandomGenerator; +import junit.framework.TestCase; + +public class OInfoCalculatorGaussianTester extends TestCase { + + /** + * For two variables, O-info is zero + */ + public void testTwoVariables() throws Exception { + double[][] cov = new double[][] {{1, 0.5}, {0.5, 1}}; + + OInfoCalculatorGaussian oCalc = new OInfoCalculatorGaussian(); + oCalc.initialise(2); + oCalc.setCovariance(cov); + double oinfo = oCalc.computeAverageLocalOfObservations(); + + assertEquals(oinfo, 0, 1e-6); + } + + + /** + * For factorisable pairwise interactions, O-info is zero + */ + public void testPairwise() throws Exception { + double[][] cov = new double[][] {{ 1, 0.5, 0, 0}, + {0.5, 1, 0, 0}, + { 0, 0, 1, 0.5}, + { 0, 0, 0.5, 1}}; + + OInfoCalculatorGaussian oCalc = new OInfoCalculatorGaussian(); + oCalc.initialise(4); + oCalc.setCovariance(cov); + double oinfo = oCalc.computeAverageLocalOfObservations(); + + assertEquals(oinfo, 0, 1e-6); + } + + /** + * Compare against the direct calculation of O-info as a sum of entropies using the + * entropy calculator. + */ + public void testCompareWithEntropy() throws Exception { + double[][] cov = new double[][] {{1, 0.4, 0.3}, {0.4, 1, 0.2}, {0.3, 0.2, 1}}; + + OInfoCalculatorGaussian oCalc = new OInfoCalculatorGaussian(); + oCalc.initialise(3); + oCalc.setCovariance(cov); + double oinfo = oCalc.computeAverageLocalOfObservations(); + + // Calculate using an entropy calculator and picking submatrices manually + EntropyCalculatorMultiVariateGaussian hCalc = new EntropyCalculatorMultiVariateGaussian(); + hCalc.initialise(3); + hCalc.setCovariance(cov); + double oinfo_hCalc = hCalc.computeAverageLocalOfObservations(); + + hCalc.initialise(2); + hCalc.setCovariance(MatrixUtils.selectRowsAndColumns(cov, new int[] {0,1}, new int[] {0,1})); + oinfo_hCalc -= hCalc.computeAverageLocalOfObservations(); + hCalc.initialise(2); + hCalc.setCovariance(MatrixUtils.selectRowsAndColumns(cov, new int[] {0,2}, new int[] {0,2})); + oinfo_hCalc -= hCalc.computeAverageLocalOfObservations(); + hCalc.initialise(2); + hCalc.setCovariance(MatrixUtils.selectRowsAndColumns(cov, new int[] {1,2}, new int[] {1,2})); + oinfo_hCalc -= hCalc.computeAverageLocalOfObservations(); + + hCalc.initialise(1); + hCalc.setCovariance(new double[][] {{cov[0][0]}}); + oinfo_hCalc += hCalc.computeAverageLocalOfObservations(); + hCalc.initialise(1); + hCalc.setCovariance(new double[][] {{cov[1][1]}}); + oinfo_hCalc += hCalc.computeAverageLocalOfObservations(); + hCalc.initialise(1); + hCalc.setCovariance(new double[][] {{cov[2][2]}}); + oinfo_hCalc += hCalc.computeAverageLocalOfObservations(); + + assertEquals(oinfo, oinfo_hCalc, 1e-6); + + } + + /** + * Confirm that the local values average correctly back to the average value + */ + public void testLocalsAverageCorrectly() throws Exception { + + int dimensions = 4; + int timeSteps = 1000; + OInfoCalculatorGaussian oCalc = new OInfoCalculatorGaussian(); + oCalc.initialise(dimensions); + + // generate some random data + RandomGenerator rg = new RandomGenerator(); + double[][] data = rg.generateNormalData(timeSteps, dimensions, + 0, 1); + + oCalc.setObservations(data); + + double oinfo = oCalc.computeAverageLocalOfObservations(); + double[] oLocal = oCalc.computeLocalOfPreviousObservations(); + + System.out.printf("Average was %.5f\n", oinfo); + + assertEquals(oinfo, MatrixUtils.mean(oLocal), 0.00001); + } + + /** + * Confirm that for 2D all local values equal zero + */ + public void testLocalsEqualZero() throws Exception { + + int dimensions = 2; + int timeSteps = 100; + OInfoCalculatorGaussian oCalc = new OInfoCalculatorGaussian(); + oCalc.initialise(dimensions); + + // generate some random data + RandomGenerator rg = new RandomGenerator(); + double[][] data = rg.generateNormalData(timeSteps, dimensions, + 0, 1); + + oCalc.setObservations(data); + double[] oLocal = oCalc.computeLocalOfPreviousObservations(); + + for (int t = 0; t < timeSteps; t++) { + assertEquals(oLocal[t], 0, 0.00001); + } + } + +} + diff --git a/java/unittests/infodynamics/measures/continuous/gaussian/SInfoCalculatorGaussianTester.java b/java/unittests/infodynamics/measures/continuous/gaussian/SInfoCalculatorGaussianTester.java new file mode 100644 index 0000000..bc33ad7 --- /dev/null +++ b/java/unittests/infodynamics/measures/continuous/gaussian/SInfoCalculatorGaussianTester.java @@ -0,0 +1,129 @@ +/* + * 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 . + */ + +package infodynamics.measures.continuous.gaussian; + +import infodynamics.utils.MatrixUtils; +import infodynamics.utils.RandomGenerator; +import junit.framework.TestCase; + +public class SInfoCalculatorGaussianTester extends TestCase { + + /** + * For two variables, S-info is twice the MI between them + */ + public void testTwoVariables() throws Exception { + double[][] cov = new double[][] {{1, 0.5}, {0.5, 1}}; + + SInfoCalculatorGaussian sCalc = new SInfoCalculatorGaussian(); + sCalc.initialise(2); + sCalc.setCovariance(cov); + double sinfo = sCalc.computeAverageLocalOfObservations(); + + MutualInfoCalculatorMultiVariateGaussian miCalc = new MutualInfoCalculatorMultiVariateGaussian(); + miCalc.initialise(1,1); + miCalc.setCovariance(cov, false); + double mi = miCalc.computeAverageLocalOfObservations(); + + assertEquals(sinfo, 2*mi, 1e-6); + } + + + /** + * Compare against the direct calculation of S-info as a sum of mutual informations. + */ + public void testCompareWithEntropy() throws Exception { + double[][] cov = new double[][] {{1, 0.4, 0.3}, {0.4, 1, 0.2}, {0.3, 0.2, 1}}; + + SInfoCalculatorGaussian sCalc = new SInfoCalculatorGaussian(); + sCalc.initialise(3); + sCalc.setCovariance(cov); + double sinfo = sCalc.computeAverageLocalOfObservations(); + + // Calculate using a mutual info calculator and picking submatrices manually + MutualInfoCalculatorMultiVariateGaussian miCalc = new MutualInfoCalculatorMultiVariateGaussian(); + miCalc.initialise(2,1); + miCalc.setCovariance(cov, false); + double sinfo_miCalc = miCalc.computeAverageLocalOfObservations(); + + miCalc.initialise(2,1); + miCalc.setCovariance(MatrixUtils.selectRowsAndColumns(cov, new int[] {2,0,1}, new int[] {2,0,1}), false); + sinfo_miCalc += miCalc.computeAverageLocalOfObservations(); + miCalc.initialise(2,1); + miCalc.setCovariance(MatrixUtils.selectRowsAndColumns(cov, new int[] {1,2,0}, new int[] {1,2,0}), false); + sinfo_miCalc += miCalc.computeAverageLocalOfObservations(); + + assertEquals(sinfo, sinfo_miCalc, 1e-6); + + } + + /** + * Confirm that the local values average correctly back to the average value + */ + public void testLocalsAverageCorrectly() throws Exception { + + int dimensions = 4; + int timeSteps = 1000; + SInfoCalculatorGaussian sCalc = new SInfoCalculatorGaussian(); + sCalc.initialise(dimensions); + + // generate some random data + RandomGenerator rg = new RandomGenerator(); + double[][] data = rg.generateNormalData(timeSteps, dimensions, + 0, 1); + + sCalc.setObservations(data); + + double sinfo = sCalc.computeAverageLocalOfObservations(); + double[] sLocal = sCalc.computeLocalOfPreviousObservations(); + + System.out.printf("Average was %.5f\n", sinfo); + + assertEquals(sinfo, MatrixUtils.mean(sLocal), 0.00001); + } + + /** + * Confirm that for 2D all local values equal zero + */ + public void testLocalsEqualMI() throws Exception { + + int dimensions = 2; + int timeSteps = 100; + SInfoCalculatorGaussian sCalc = new SInfoCalculatorGaussian(); + sCalc.initialise(dimensions); + + // generate some random data + RandomGenerator rg = new RandomGenerator(); + double[][] data = rg.generateNormalData(timeSteps, dimensions, + 0, 1); + + sCalc.setObservations(data); + double[] sLocal = sCalc.computeLocalOfPreviousObservations(); + + MutualInfoCalculatorMultiVariateGaussian miCalc = new MutualInfoCalculatorMultiVariateGaussian(); + miCalc.initialise(1, 1); + miCalc.setObservations(MatrixUtils.selectColumn(data, 0), MatrixUtils.selectColumn(data, 1)); + double[] miLocal = miCalc.computeLocalOfPreviousObservations(); + + for (int t = 0; t < timeSteps; t++) { + assertEquals(sLocal[t], 2*miLocal[t], 0.00001); + } + } + +} +