From b3abd382aa63246c24909eb5bc975d918ee3563e Mon Sep 17 00:00:00 2001 From: Pedro Mediano Date: Sun, 24 Jan 2021 22:10:14 +0000 Subject: [PATCH] Added abstract parent class for multivariate IT measures on discrete data. --- ...iVariateInfoMeasureCalculatorDiscrete.java | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100755 java/source/infodynamics/measures/discrete/MultiVariateInfoMeasureCalculatorDiscrete.java diff --git a/java/source/infodynamics/measures/discrete/MultiVariateInfoMeasureCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/MultiVariateInfoMeasureCalculatorDiscrete.java new file mode 100755 index 0000000..1d3f826 --- /dev/null +++ b/java/source/infodynamics/measures/discrete/MultiVariateInfoMeasureCalculatorDiscrete.java @@ -0,0 +1,279 @@ +/* + * 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.discrete; + +import infodynamics.utils.MathsUtils; +import infodynamics.utils.MatrixUtils; + +/** + * Implements a base class with common functionality for child class + * implementations of multivariate information measures. + * + *

Multivariate information measures are functionals of probability + * distributions over R^n, and typical examples include multi-information + * (a.k.a. total correlation), dual total correlation, O-information, and connected + * information.

+ * + *

Usage of child classes is intended to follow this paradigm:

+ *
    + *
  1. Construct the calculator;
  2. + *
  3. Initialise the calculator using {@link #initialise()};
  4. + *
  5. Provide the observations/samples for the calculator + * to set up the PDFs, using one or more calls to + * sets of {@link #addObservations(int[][], int[])} methods, then
  6. + *
  7. Compute the required quantities, being one or more of: + *
      + *
    • the average measure: {@link #computeAverageLocalOfObservations()};
    • + *
    + *
  8. + *
  9. + * Return to step 2 to re-use the calculator on a new data set. + *
  10. + *
+ * + *

References:
+ *

+ * + * @author Pedro A.M. Mediano (email, + * www) + */ +public abstract class MultiVariateInfoMeasureCalculatorDiscrete + extends InfoMeasureCalculatorDiscrete { + + /** + * Count of occurrences of each joint state in the provided observations. + */ + protected int[] jointCount = null; + + /** + * Count of occurrences of each state of each variable in the provided + * observations. + * + * For a given variable v and state i, + * smallMarginalCounts[v][i] counts how many times variable + * v was observed in state i, + */ + protected int[][] smallMarginalCounts = null; // marginalCounts[marginalIndex][state] + + /** + * Count of occurrences of each state of each (D-1)-dimensional marginal in + * the provided observations. + * + * For a given variable v and state i, + * bigMarginalCounts[v][i] counts how many times the _rest_ of + * the system, excluding variable v, was observed in state i, + */ + protected int[][] bigMarginalCounts = null; + + /** + * Number of variables in the system. + */ + protected int numVars; + + /** + * Number of possible states of the whole system. + */ + protected int jointStates; + + /** + * Whether the first local value has been checked. (Used to initialise some variables + * related to computation of local values. + */ + protected boolean checkedFirst = false; + + /** + * Abstract constructor (to be called by child classes). + * + * @param base number of symbols for each variable. + * E.g. binary variables are in base-2. + * @param numVars numbers of joint variables that the measure + * will be computed over. + */ + protected MultiVariateInfoMeasureCalculatorDiscrete(int base, int numVars) { + super(base); + this.numVars = numVars; + jointStates = MathsUtils.power(base, numVars); + try { + jointCount = new int[jointStates]; + smallMarginalCounts = new int[numVars][base]; + bigMarginalCounts = new int[numVars][jointStates]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a RuntimeException + throw new RuntimeException("Requested memory for the base " + + base + " with " + numVars + + " variables is too large for the JVM at this time", e); + } + } + + @Override + public void initialise(){ + super.initialise(); + MatrixUtils.fill(jointCount, 0); + MatrixUtils.fill(smallMarginalCounts, 0); + MatrixUtils.fill(bigMarginalCounts, 0); + } + + /** + * Given multiple time samples of a homogeneous array of variables (states), + * add the observations of all sets of numVars of these + * Do this for every time point + * + * @param states 2D array of values of an array of variables + * at many observations (first index is time, second is variable index) + */ + public void addObservations(int[][] states) throws Exception { + int[] jointStates = MatrixUtils.computeCombinedValues(states, base); + for (int t = 0; t < states.length; t++) { + for (int i = 0; i < numVars; i++) { + // Extract values of the 1D and the (N-1)D marginals + int thisValue = states[t][i]; + int bigMarginalState = computeBigMarginalState(jointStates[t], i, thisValue); + + // Update counts + bigMarginalCounts[i][bigMarginalState]++; + smallMarginalCounts[i][thisValue]++; + } + jointCount[jointStates[t]]++; + observations++; + } + } + + @Override + public double computeAverageLocalOfObservations() { + + int[] jointTuple = new int[numVars]; + checkedFirst = false; + try { + average = computeForGivenTupleFromVarIndex(jointTuple, 0); + } catch (Exception e) { + System.out.println("Something went wrong during the calculation."); + average = -1; + } + + return average; + } + + /** + * Private utility to compute the contribution to the measure for all tuples + * starting with tuple[0..(fromIndex-1)]. + * + * @param tuple + * @param fromIndex + * @return + */ + public double computeForGivenTupleFromVarIndex(int[] tuple, int fromIndex) throws Exception { + double miCont = 0; + if (fromIndex == numVars) { + // The whole tuple is filled in, so compute the contribution to the MI from this tuple + int jointValue = MatrixUtils.computeCombinedValues(new int[][] {tuple}, base)[0]; + + if (jointCount[jointValue] == 0) { + // This joint state does not occur, so it makes no contribution here + return 0; + } + + double jointProb = (double) jointCount[jointValue] / (double) observations; + double localValue = computeLocalValueForTuple(tuple, jointValue); + miCont = jointProb * localValue; + + } else { + // Fill out the next part of the tuple and make the recursive calls + for (int v = 0; v < base; v++) { + tuple[fromIndex] = v; + miCont += computeForGivenTupleFromVarIndex(tuple, fromIndex + 1); + } + } + return miCont; + } + + /** + * Shortcut method to initialise the calculator, add observations and compute + * the average measure in one line. + * + * @param state series of multivariate observations + * (first index is time or observation index, second is variable number) + */ + public double compute(int[][] states) throws Exception { + initialise(); + addObservations(states); + return computeAverageLocalOfObservations(); + } + + /** + * Internal method to update maximum and minimum values of local information + * measures. + * + * @param localValue instance of computed local information measure + */ + protected void checkLocals(double localValue) { + if (!checkedFirst) { + max = localValue; + min = localValue; + checkedFirst = true; + } else { + if (localValue > max) { + max = localValue; + } + if (localValue < min) { + min = localValue; + } + } + } + + /** + * Method to be implemented by all child classes to compute the local value + * of the measure for a given tuple. + * + * @param tuple state of the system at a given time (index is variable number) + * @param jointValue int representing the state of the system + */ + protected abstract double computeLocalValueForTuple(int[] tuple, int jointValue) + throws Exception; + + /** + * Method to be implemented by all child classes to compute the local value + * of the measure for a given tuple. + * + * @param tuple state of the system at a given time (index is variable number) + */ + protected double computeLocalValueForTuple(int[] tuple) throws Exception { + int jointValue = MatrixUtils.computeCombinedValues(new int[][] {tuple}, base)[0]; + return computeLocalValueForTuple(tuple, jointValue); + } + + /** + * Small utility function to compute the state of the system excluding one variable. + * + * @param jointState state of the full system + * @param varIdx index of the variable to be excluded + * @param varValue value of the variable in question in the system state + */ + protected int computeBigMarginalState(int jointState, int varIdx, int varValue) { + int bigMarginalState = jointState - varValue*MathsUtils.power(base, numVars - varIdx - 1); + return bigMarginalState; + } + +} +