Javadocs finalised for discrete package. Also added some new methods to ChannelCalculator, SingleAgentMeasure, EntropyRateCalculator, ActiveInfoStorageCalculator made to inherit/extend from SingleAgentMeasureInContextOfPastCalculator.

This commit is contained in:
joseph.lizier 2014-08-13 14:42:56 +00:00
parent e27c9b0882
commit 8ea3368224
18 changed files with 2254 additions and 1807 deletions

View File

@ -19,100 +19,83 @@
package infodynamics.measures.discrete;
import infodynamics.utils.EmpiricalMeasurementDistribution;
import infodynamics.utils.MathsUtils;
import infodynamics.utils.MatrixUtils;
import infodynamics.utils.RandomGenerator;
/**
* Usage:
* 1. Continuous accumulation of observations:
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations()
* 2. Standalone:
* Call: localActiveInformation()
*
* @author Joseph Lizier
* <p>Active Information Storage calculator for univariate discrete (int[]) data.
* See definition of Active Information Storage (AIS) by Lizier et al. below.
* Basically, AIS is the mutual information between the past <i>state</i>
* of a time-series process <i>X</i> and its next value. The past <i>state</i> at time <code>n</code>
* is represented by an embedding vector of <code>k</code> values from <code>X_n</code> backwards,
* each separated by <code>\tau</code> steps, giving
* <code><b>X^k_n</b> = [ X_{n-(k-1)\tau}, ... , X_{n-\tau}, X_n]</code>.
* We call <code>k</code> the embedding dimension, and <code>\tau</code>
* the embedding delay (only delay = 1 is implemented at the moment).
* AIS is then the mutual information between <b>X^k_n</b> and X_{n+1}.</p>
*
* <p>Usage of the class is intended to follow this paradigm:</p>
* <ol>
* <li>Construct the calculator: {@link #ActiveInformationCalculator(int, int)};</li>
* <li>Initialise the calculator using {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* sets of {@link #addObservations(int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average entropy: {@link #computeAverageLocalOfObservations()};</li>
* <li>local entropy values, such as {@link #computeLocal(int[])};</li>
* <li>and variants of these.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[])},
* {@link #computeAverageLocal(int[])} etc.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p><b>References:</b><br/>
* <ul>
* <li>J.T. Lizier, M. Prokopenko and A.Y. Zomaya,
* <a href="http://dx.doi.org/10.1016/j.ins.2012.04.016">
* "Local measures of information storage in complex distributed computation"</a>,
* Information Sciences, vol. 208, pp. 39-54, 2012.</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class ActiveInformationCalculator {
public class ActiveInformationCalculator extends SingleAgentMeasureInContextOfPastCalculator {
private double average = 0.0;
private double max = 0.0;
private double min = 0.0;
private int observations = 0;
private int k = 0; // history length k.
private int base = 0; // number of individual states.
private int[][] jointCount = null; // Count for (i[t+1], i[t]) tuples
private int[] prevCount = null; // Count for i[t]
private int[] nextCount = null; // Count for i[t+1]
private int[] maxShiftedValue = null; // states * (base^(history-1))
private int base_power_k = 0;
private double log_base = 0;
/**
* User was formerly forced to create new instances through this factory method.
* Retained for backwards compatibility.
*
* @param base
* @param history
*
* @deprecated
* @return
*/
public static ActiveInformationCalculator newInstance(int base, int history) {
return new ActiveInformationCalculator(base, history);
}
/**
* Construct a new instance
*
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @param history embedded history length of the destination to condition on -
* this is k in Schreiber's notation.
*/
public ActiveInformationCalculator(int base, int history) {
super();
this.base = base;
k = history;
base_power_k = MathsUtils.power(base, k);
log_base = Math.log(base);
if (history < 1) {
throw new RuntimeException("History k " + history + " is not >= 1 for Active info storage Calculator");
}
if (k > Math.log(Integer.MAX_VALUE) / log_base) {
throw new RuntimeException("Base and history combination too large");
}
// Create storage for counts of observations
jointCount = new int[base][base_power_k];
prevCount = new int[base_power_k];
nextCount = new int[base];
// Create constants for tracking prevValues
maxShiftedValue = new int[base];
for (int v = 0; v < base; v++) {
maxShiftedValue[v] = v * MathsUtils.power(base, k-1);
}
super(base, history);
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
*
*/
public void initialise(){
average = 0.0;
max = 0.0;
min = 0.0;
observations = 0;
MatrixUtils.fill(jointCount, 0);
MatrixUtils.fill(prevCount, 0);
MatrixUtils.fill(nextCount, 0);
}
/**
* Add observations in to our estimates of the pdfs.
*
* @param states time series of agent states
*/
@Override
public void addObservations(int states[]) {
int timeSteps = states.length;
// increment the count of observations:
@ -130,8 +113,8 @@ public class ActiveInformationCalculator {
for (int t = k; t < timeSteps; t++) {
// Add to the count for this particular transition:
nextVal = states[t];
jointCount[nextVal][prevVal]++;
prevCount[prevVal]++;
nextPastCount[nextVal][prevVal]++;
pastCount[prevVal]++;
nextCount[nextVal]++;
// Update the previous value:
prevVal -= maxShiftedValue[states[t-k]];
@ -140,13 +123,7 @@ public class ActiveInformationCalculator {
}
}
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd index is agent number
*/
@Override
public void addObservations(int states[][]) {
int rows = states.length;
int columns = states[0].length;
@ -170,8 +147,8 @@ public class ActiveInformationCalculator {
// Add to the count for this particular transition:
// (cell's assigned as above)
nextVal = states[r][c];
jointCount[nextVal][prevVal[c]]++;
prevCount[prevVal[c]]++;
nextPastCount[nextVal][prevVal[c]]++;
pastCount[prevVal[c]]++;
nextCount[nextVal]++;
// Update the previous value:
prevVal[c] -= maxShiftedValue[states[r-k][c]];
@ -181,13 +158,7 @@ public class ActiveInformationCalculator {
}
}
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
*/
@Override
public void addObservations(int states[][][]) {
int timeSteps = states.length;
if (timeSteps == 0) {
@ -221,8 +192,8 @@ public class ActiveInformationCalculator {
// Add to the count for this particular transition:
// (cell's assigned as above)
nextVal = states[t][r][c];
jointCount[nextVal][prevVal[r][c]]++;
prevCount[prevVal[r][c]]++;
nextPastCount[nextVal][prevVal[r][c]]++;
pastCount[prevVal[r][c]]++;
nextCount[nextVal]++;
// Update the previous value:
prevVal[r][c] -= maxShiftedValue[states[t-k][r][c]];
@ -233,14 +204,7 @@ public class ActiveInformationCalculator {
}
}
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd index is agent number
*/
@Override
public void addObservations(int states[][], int col) {
int rows = states.length;
// increment the count of observations:
@ -260,8 +224,8 @@ public class ActiveInformationCalculator {
// Add to the count for this particular transition:
// (cell's assigned as above)
nextVal = states[r][col];
jointCount[nextVal][prevVal]++;
prevCount[prevVal]++;
nextPastCount[nextVal][prevVal]++;
pastCount[prevVal]++;
nextCount[nextVal]++;
// Update the previous value:
prevVal -= maxShiftedValue[states[r-k][col]];
@ -270,14 +234,7 @@ public class ActiveInformationCalculator {
}
}
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
*/
@Override
public void addObservations(int states[][][], int agentIndex1, int agentIndex2) {
int timeSteps = states.length;
// increment the count of observations:
@ -297,8 +254,8 @@ public class ActiveInformationCalculator {
// Add to the count for this particular transition:
// (cell's assigned as above)
nextVal = states[t][agentIndex1][agentIndex2];
jointCount[nextVal][prevVal]++;
prevCount[prevVal]++;
nextPastCount[nextVal][prevVal]++;
pastCount[prevVal]++;
nextCount[nextVal]++;
// Update the previous value:
prevVal -= maxShiftedValue[states[t-k][agentIndex1][agentIndex2]];
@ -307,12 +264,7 @@ public class ActiveInformationCalculator {
}
}
/**
* Returns the average local active information storage from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public double computeAverageLocalOfObservations() {
double mi = 0.0;
double miCont = 0.0;
@ -324,9 +276,9 @@ public class ActiveInformationCalculator {
double p_next = (double) nextCount[nextVal] / (double) observations;
for (int prevVal = 0; prevVal < base_power_k; prevVal++) {
// compute p_prev
double p_prev = (double) prevCount[prevVal] / (double) observations;
double p_prev = (double) pastCount[prevVal] / (double) observations;
// compute p(prev, next)
double p_joint = (double) jointCount[nextVal][prevVal] / (double) observations;
double p_joint = (double) nextPastCount[nextVal][prevVal] / (double) observations;
// Compute MI contribution:
if (p_joint > 0.0) {
double logTerm = p_joint / (p_next * p_prev);
@ -352,7 +304,7 @@ public class ActiveInformationCalculator {
* Returns the average local entropy rate from
* the observed values which have been passed in previously.
*
* @return
* @return average entropy rate
*/
public double computeAverageLocalEntropyRateOfObservations() {
double entRate = 0.0;
@ -361,9 +313,9 @@ public class ActiveInformationCalculator {
for (int nextVal = 0; nextVal < base; nextVal++) {
for (int prevVal = 0; prevVal < base_power_k; prevVal++) {
// compute p_prev
double p_prev = (double) prevCount[prevVal] / (double) observations;
double p_prev = (double) pastCount[prevVal] / (double) observations;
// compute p(prev, next)
double p_joint = (double) jointCount[nextVal][prevVal] / (double) observations;
double p_joint = (double) nextPastCount[nextVal][prevVal] / (double) observations;
// Compute entropy rate contribution:
if (p_joint > 0.0) {
double logTerm = p_joint / p_prev;
@ -382,29 +334,24 @@ public class ActiveInformationCalculator {
/**
* Computes local active info storage for the given (single)
* specific values
* specific values.
*
* @param destNext
* @param destPast
* @param sourceCurrent
* @return
* See {@link TransferEntropyCalculator#getPastCount(int)} for how the
* joint embedded values representing the past are calculated.
*
* @param next next value of the variable
* @param past int representing the joint state of the past of the variable x[n]^k
* @return local active info storage value
*/
public double computeLocalFromPreviousObservations(int next, int past){
double logTerm = ( (double) jointCount[next][past] ) /
double logTerm = ( (double) nextPastCount[next][past] ) /
( (double) nextCount[next] *
(double) prevCount[past] );
(double) pastCount[past] );
logTerm *= (double) observations;
return Math.log(logTerm) / log_base;
}
/**
* Computes local active information storage for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
*
* @param states time series of states
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[]){
int timeSteps = states.length;
@ -425,9 +372,9 @@ public class ActiveInformationCalculator {
double logTerm = 0.0;
for (int t = k; t < timeSteps; t++) {
nextVal = states[t];
logTerm = ( (double) jointCount[nextVal][prevVal] ) /
logTerm = ( (double) nextPastCount[nextVal][prevVal] ) /
( (double) nextCount[nextVal] *
(double) prevCount[prevVal] );
(double) pastCount[prevVal] );
// Now account for the fact that we've
// just used counts rather than probabilities,
// and we've got two counts on the bottom
@ -451,15 +398,7 @@ public class ActiveInformationCalculator {
}
/**
* Computes local active information storage for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd index is agent number
* @return
*/
@Override
public double[][] computeLocalFromPreviousObservations(int states[][]){
int rows = states.length;
int columns = states[0].length;
@ -484,9 +423,9 @@ public class ActiveInformationCalculator {
for (int r = k; r < rows; r++) {
for (int c = 0; c < columns; c++) {
nextVal = states[r][c];
logTerm = ( (double) jointCount[nextVal][prevVal[c]] ) /
logTerm = ( (double) nextPastCount[nextVal][prevVal[c]] ) /
( (double) nextCount[nextVal] *
(double) prevCount[prevVal[c]] );
(double) pastCount[prevVal[c]] );
// Now account for the fact that we've
// just used counts rather than probabilities,
// and we've got two counts on the bottom
@ -511,15 +450,7 @@ public class ActiveInformationCalculator {
}
/**
* Computes local active information storage for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @return
*/
@Override
public double[][][] computeLocalFromPreviousObservations(int states[][][]){
int timeSteps = states.length;
int agentRows = states[0].length;
@ -548,9 +479,9 @@ public class ActiveInformationCalculator {
for (int r = 0; r < agentRows; r++) {
for (int c = 0; c < agentColumns; c++) {
nextVal = states[t][r][c];
logTerm = ( (double) jointCount[nextVal][prevVal[r][c]] ) /
logTerm = ( (double) nextPastCount[nextVal][prevVal[r][c]] ) /
( (double) nextCount[nextVal] *
(double) prevCount[prevVal[r][c]] );
(double) pastCount[prevVal[r][c]] );
// Now account for the fact that we've
// just used counts rather than probabilities,
// and we've got two counts on the bottom
@ -575,15 +506,7 @@ public class ActiveInformationCalculator {
return localActive;
}
/**
* Computes local active information storage for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd index is agent number
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[][], int col){
int rows = states.length;
//int columns = states[0].length;
@ -605,9 +528,9 @@ public class ActiveInformationCalculator {
double logTerm = 0.0;
for (int r = k; r < rows; r++) {
nextVal = states[r][col];
logTerm = ( (double) jointCount[nextVal][prevVal] ) /
logTerm = ( (double) nextPastCount[nextVal][prevVal] ) /
( (double) nextCount[nextVal] *
(double) prevCount[prevVal] );
(double) pastCount[prevVal] );
// Now account for the fact that we've
// just used counts rather than probabilities,
// and we've got two counts on the bottom
@ -631,16 +554,9 @@ public class ActiveInformationCalculator {
}
/**
* Computes local active information storage for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @return
*/
public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){
@Override
public double[] computeLocalFromPreviousObservations(int states[][][],
int agentIndex1, int agentIndex2){
int timeSteps = states.length;
//int columns = states[0].length;
@ -661,9 +577,9 @@ public class ActiveInformationCalculator {
double logTerm = 0.0;
for (int t = k; t < timeSteps; t++) {
nextVal = states[t][agentIndex1][agentIndex2];
logTerm = ( (double) jointCount[nextVal][prevVal] ) /
logTerm = ( (double) nextPastCount[nextVal][prevVal] ) /
( (double) nextCount[nextVal] *
(double) prevCount[prevVal] );
(double) pastCount[prevVal] );
// Now account for the fact that we've
// just used counts rather than probabilities,
// and we've got two counts on the bottom
@ -688,207 +604,29 @@ public class ActiveInformationCalculator {
}
/**
* Standalone routine to
* compute local active information storage across an
* array of the states of homogeneous agents
* Return an array of local values.
* First history rows are zeros
* Generate a bootstrapped distribution of what the AIS would look like,
* under a null hypothesis that the past <code>k</code> values of our
* samples had no relation to the next value.
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - time series array of states
* @return
*/
public double[] computeLocal(int states[]) {
initialise();
addObservations(states);
return computeLocalFromPreviousObservations(states);
}
/**
* Standalone routine to
* compute local active information storage across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* <p>See Section II.E "Statistical significance testing" of
* the JIDT paper below for a description of how this is done for
* an MI (like the AIS).
* </p>
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - 2D array of states
* @return
*/
public double[][] computeLocal(int states[][]) {
initialise();
addObservations(states);
return computeLocalFromPreviousObservations(states);
}
/**
* Standalone routine to
* compute local active information storage across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* <p>Note that if several disjoint time-series have been added
* as observations using {@link #addObservations(int[], int[])} etc.,
* then these separate "trials" will be mixed up in the generation
* of surrogates here.</p>
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - array of states - 1st dimension is time, 2nd and 3rd are agent indices
* @return
*/
public double[][][] computeLocal(int states[][][]) {
initialise();
addObservations(states);
return computeLocalFromPreviousObservations(states);
}
/**
* Standalone routine to
* compute average local active information storage across an
* array of the states of homogeneous agents
* Return the averagen
* <p>This method (in contrast to {@link #computeSignificance(int[][])})
* creates <i>random</i> shufflings of the next values for the surrogate AIS
* calculations.</p>
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - narray of states
* @return
*/
public double computeAverageLocal(int states[]) {
initialise();
addObservations(states);
return computeAverageLocalOfObservations();
}
/**
* Standalone routine to
* compute average local active information storage across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* This method to be called for homogeneous agents only
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - 2D array of states
* @return
*/
public double computeAverageLocal(int states[][]) {
initialise();
addObservations(states);
return computeAverageLocalOfObservations();
}
/**
* Standalone routine to
* compute average local active information storage across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* This method to be called for homogeneous agents only
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - array of states - 1st dimension is time, 2nd and 3rd are agent indices
* @return
*/
public double computeAverageLocal(int states[][][]) {
initialise();
addObservations(states);
return computeAverageLocalOfObservations();
}
/**
* Standalone routine to
* compute local active information storage for one agent in a 2D spatiotemporal
* array of the states of agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method should be used for heterogeneous agents
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - 2D array of states
* @param col - column number of the agent in the states array
* @return
*/
public double[] computeLocal(int states[][], int col) {
initialise();
addObservations(states, col);
return computeLocalFromPreviousObservations(states, col);
}
/**
* Standalone routine to
* compute local active information storage for one agent in a 2D spatiotemporal
* array of the states of agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method should be used for heterogeneous agents
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - array of states - 1st dimension is time, 2nd and 3rd are agent indices
* @param agentIndex1 row index of agent
* @param agentIndex2 column index of agent
* @return
*/
public double[] computeLocal(int states[][][], int agentIndex1, int agentIndex2) {
initialise();
addObservations(states, agentIndex1, agentIndex2);
return computeLocalFromPreviousObservations(states, agentIndex1, agentIndex2);
}
/**
* Standalone routine to
* compute average local active information storage
* for a single agent
* Returns the average
* This method suitable for heterogeneous agents
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - 2D array of states
* @param col - column number of the agent in the states array
* @return
*/
public double computeAverageLocal(int states[][], int col) {
initialise();
addObservations(states, col);
return computeAverageLocalOfObservations();
}
/**
* Standalone routine to
* compute average local active information storage
* for a single agent
* Returns the average
* This method suitable for heterogeneous agents
*
* @param history - parameter k
* @param maxEmbeddingLength - base of the states
* @param states - array of states - 1st dimension is time, 2nd and 3rd are agent indices
* @param agentIndex1 row index of agent
* @param agentIndex2 column index of agent
* @return
*/
public double computeAverageLocal(int states[][][], int agentIndex1, int agentIndex2) {
initialise();
addObservations(states, agentIndex1, agentIndex2);
return computeAverageLocalOfObservations();
}
/**
* Compute the significance of obtaining the given average from the given observations
*
* @param numPermutationsToCheck number of new orderings of the source values to compare against
* @return
* @param numPermutationsToCheck number of surrogate samples to bootstrap
* to generate the distribution.
* @return the distribution of AIS scores under this null hypothesis.
* @see "J.T. Lizier, 'JIDT: An information-theoretic
* toolkit for studying the dynamics of complex systems', 2014."
*/
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) {
RandomGenerator rg = new RandomGenerator();
@ -898,10 +636,38 @@ public class ActiveInformationCalculator {
}
/**
* Compute the significance of obtaining the given average from the given observations
* Generate a bootstrapped distribution of what the AIS would look like,
* under a null hypothesis that the previous <code>k</code> values of our
* samples had no relation to the next value in the time-series.
*
* @param newOrderings the reorderings to use
* @return
* <p>See Section II.E "Statistical significance testing" of
* the JIDT paper below for a description of how this is done for AIS
* as a mutual information. Basically, the marginal PDFs
* of the past <code>k</code> values, and that of the next value,
* are preserved, while their joint PDF is destroyed, and the
* distribution of AIS under these conditions is generated.</p>
*
* <p>Note that if several disjoint time-series have been added
* as observations using {@link #addObservations(double[])} etc.,
* then these separate "trials" will be mixed up in the generation
* of surrogates here.</p>
*
* <p>This method (in contrast to {@link #computeSignificance(int)})
* allows the user to specify how to construct the surrogates,
* such that repeatable results may be obtained.</p>
*
* @param newOrderings a specification of how to shuffle the next values
* to create the surrogates to generate the distribution with. The first
* index is the permutation number (i.e. newOrderings.length is the number
* of surrogate samples we use to bootstrap to generate the distribution here.)
* Each array newOrderings[i] should be an array of length N (where
* would be the value returned by {@link #getNumObservations()}),
* containing a permutation of the values in 0..(N-1).
* @return the distribution of AIS scores under this null hypothesis.
* @see "J.T. Lizier, 'JIDT: An information-theoretic
* toolkit for studying the dynamics of complex systems', 2014."
* @throws Exception where the length of each permutation in newOrderings
* is not equal to the number N samples that were previously supplied.
*/
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) {
double actualMI = computeAverageLocalOfObservations();
@ -913,8 +679,8 @@ public class ActiveInformationCalculator {
int[] nextValues = new int[observations];
int t_prev = 0;
int t_next = 0;
for (int prevVal = 0; prevVal < prevCount.length; prevVal++) {
int numberOfSamplesPrev = prevCount[prevVal];
for (int prevVal = 0; prevVal < pastCount.length; prevVal++) {
int numberOfSamplesPrev = pastCount[prevVal];
MatrixUtils.fill(prevValues, prevVal, t_prev, numberOfSamplesPrev);
t_prev += numberOfSamplesPrev;
}
@ -928,7 +694,7 @@ public class ActiveInformationCalculator {
ais2 = new ActiveInformationCalculator(base, k);
ais2.initialise();
ais2.observations = observations;
ais2.prevCount = prevCount;
ais2.pastCount = pastCount;
ais2.nextCount = nextCount;
int countWhereMIIsMoreSignificantThanOriginal = 0;
EmpiricalMeasurementDistribution measDistribution = new EmpiricalMeasurementDistribution(numPermutationsToCheck);
@ -936,9 +702,9 @@ public class ActiveInformationCalculator {
// Generate a new re-ordered data set for the next variable
int[] newDataNext = MatrixUtils.extractSelectedTimePoints(nextValues, newOrderings[p]);
// compute the joint probability distribution
MatrixUtils.fill(ais2.jointCount, 0);
MatrixUtils.fill(ais2.nextPastCount, 0);
for (int t = 0; t < observations; t++) {
ais2.jointCount[newDataNext[t]][prevValues[t]]++;
ais2.nextPastCount[newDataNext[t]][prevValues[t]]++;
}
// And get an MI value for this realisation:
double newMI = ais2.computeAverageLocalOfObservations();
@ -955,20 +721,8 @@ public class ActiveInformationCalculator {
return measDistribution;
}
public double getLastAverage() {
return average;
}
public double getLastMax() {
return max;
}
public double getLastMin() {
return min;
}
/**
* Writes the current probability distribution functions
* Debug method to write the current probability distribution functions
*
* @return
*/
@ -982,9 +736,9 @@ public class ActiveInformationCalculator {
double p_next = (double) nextCount[nextVal] / (double) observations;
for (int prevVal = 0; prevVal < base_power_k; prevVal++) {
// compute p_prev
double p_prev = (double) prevCount[prevVal] / (double) observations;
double p_prev = (double) pastCount[prevVal] / (double) observations;
// compute p(prev, next)
double p_joint = (double) jointCount[nextVal][prevVal] / (double) observations;
double p_joint = (double) nextPastCount[nextVal][prevVal] / (double) observations;
// Compute MI contribution:
if (p_joint * p_next * p_prev > 0.0) {
double logTerm = p_joint / (p_next * p_prev);
@ -1006,12 +760,17 @@ public class ActiveInformationCalculator {
}
/**
* Utility function to compute the combined past values of x up to and including time step t
* Utility function to compute a unique number to represent the
* combined past values of x up to and including time step t:
* (i.e. (x_{t-k+1}, ... ,x_{t-1},x_{t}))
*
* @param x
* @param t
* @return
* See {@link TransferEntropyCalculator#getPastCount(int)} for
* how the joint value representing the past is calculated.
*
* @param x time series
* @param t time step at which to compute the combined past
* @return an int representing the joint state of the past of x, x[t]^k
*
*/
public int computePastValue(int[] x, int t) {
int pastVal = 0;
@ -1023,37 +782,53 @@ public class ActiveInformationCalculator {
}
/**
* Utility function to compute the combined past values of x up to and including time step t
* Utility function to compute a unique number to represent the
* combined past values of x (which is a column in data)
* up to and including time step t:
* (i.e. (x_{t-k+1}, ... ,x_{t-1},x_{t}))
*
* @param x
* @param agentNumber
* @param t
* @return
* See {@link TransferEntropyCalculator#getPastCount(int)} for
* how the joint value representing the past is calculated.
*
* @param data 2D time series, first index is time,
* second is variable number
* @param column column of data which is variable x
* @param t time step at which to compute the combined past
* @return an int representing the joint state of the past of x, x[t]^k
*/
public int computePastValue(int[][] x, int agentNumber, int t) {
public int computePastValue(int[][] data, int column, int t) {
int pastVal = 0;
for (int p = 0; p < k; p++) {
pastVal *= base;
pastVal += x[t - k + 1 + p][agentNumber];
pastVal += data[t - k + 1 + p][column];
}
return pastVal;
}
/**
* Utility function to compute the combined past values of x up to and including time step t
* Utility function to compute a unique number to represent the
* combined past values of x (which is a variable in data)
* up to and including time step t:
* (i.e. (x_{t-k+1}, ... ,x_{t-1},x_{t}))
*
* @param x
* @param agentNumber
* @param t
* @return
* See {@link TransferEntropyCalculator#getPastCount(int)} for
* how the joint value representing the past is calculated.
*
* @param data 3D time series, first index is time,
* second is variable row number, third is variable column number
* @param agentRow row of data for variable x
* @param agentColumn column of data for variable x
* @param t time step at which to compute the combined past
* @return an int representing the joint state of the past of x, x[t]^k
* Utility function to compute the combined past values of x up to and including time step t
* (i.e. (x_{t-k+1}, ... ,x_{t-1},x_{t}))
*/
public int computePastValue(int[][][] x, int agentRow, int agentColumn, int t) {
public int computePastValue(int[][][] data, int agentRow,
int agentColumn, int t) {
int pastVal = 0;
for (int p = 0; p < k; p++) {
pastVal *= base;
pastVal += x[t - k + 1 + p][agentRow][agentColumn];
pastVal += data[t - k + 1 + p][agentRow][agentColumn];
}
return pastVal;
}

View File

@ -22,26 +22,50 @@ import infodynamics.utils.MathsUtils;
import infodynamics.utils.MatrixUtils;
/**
* Computes entropy over blocks of consecutive states in time.
*
* <p>Block entropy calculator for univariate discrete (int[]) data
* (ie computes entropy over blocks of consecutive states in time).
* Implemented separately from the single state entropy
* calculator to allow the single state calculator
* to have optimal performance.
*
* Usage:
* 1. Continuous accumulation of observations:
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations()
* 2. Standalone:
* Call: localActiveInformation()
*
* @author Joseph Lizier
* to have optimal performance.</p>
*
* <p>Usage of the class is intended to follow this paradigm:</p>
* <ol>
* <li>Construct the calculator: {@link #BlockEntropyCalculator(int, int)};</li>
* <li>Initialise the calculator using {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* sets of {@link #addObservations(int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average entropy: {@link #computeAverageLocalOfObservations()};</li>
* <li>local entropy values, such as {@link #computeLocal(int[])};</li>
* <li>and variants of these.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[])},
* {@link #computeAverageLocal(int[])} etc.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p><b>References:</b><br/>
* <ul>
* <li>T. M. Cover and J. A. Thomas, 'Elements of Information
Theory' (John Wiley & Sons, New York, 1991).</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class BlockEntropyCalculator extends EntropyCalculator {
protected int blocksize = 0; // temporal blocksize to compute entropy over. Need initialised to 0 for changedSizes
/**
* Number of consecutive time-steps to compute entropy over.
*/
protected int blocksize = 0; // Need initialised to 0 for changedSizes
protected int[] maxShiftedValue = null; // states * (base^(blocksize-1))
protected int base_power_blocksize = 0;
@ -52,16 +76,18 @@ public class BlockEntropyCalculator extends EntropyCalculator {
*
* @param blocksize
* @param base
* @return
* @deprecated
*/
public static EntropyCalculator newInstance(int blocksize, int base) {
return new BlockEntropyCalculator(blocksize, base);
}
/**
* Construct a new instance
*
* @param blocksize
* @param base
* @param blocksize Number of consecutive time-steps to compute entropy over.
* @param base number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
*/
public BlockEntropyCalculator(int blocksize, int base) {
@ -89,22 +115,13 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
*/
@Override
public void initialise(){
super.initialise();
MatrixUtils.fill(stateCount, 0);
}
/**
* Add observations in to our estimates of the pdfs.
*
* @param states index is time
*/
@Override
public void addObservations(int states[]) {
int rows = states.length;
// increment the count of observations:
@ -133,13 +150,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
}
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd index is agent number
*/
@Override
public void addObservations(int states[][]) {
int rows = states.length;
int columns = states[0].length;
@ -174,13 +185,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
}
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd and 3rd index are agent number
*/
@Override
public void addObservations(int states[][][]) {
int timeSteps = states.length;
if (timeSteps == 0) {
@ -226,14 +231,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
}
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd index is agent number
*/
@Override
public void addObservations(int states[][], int col) {
int rows = states.length;
// increment the count of observations:
@ -262,14 +260,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
}
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd index are agent number
*/
@Override
public void addObservations(int states[][][], int agentIndex1, int agentIndex2) {
int timeSteps = states.length;
// increment the count of observations:
@ -298,12 +289,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
}
/**
* Returns the average local entropy from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public double computeAverageLocalOfObservations() {
double ent = 0.0;
double entCont = 0.0;
@ -332,15 +318,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
return ent;
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd index is agent number
* @return
*/
@Override
public double[][] computeLocalFromPreviousObservations(int states[][]){
int rows = states.length;
int columns = states[0].length;
@ -388,15 +366,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @return
*/
@Override
public double[][][] computeLocalFromPreviousObservations(int states[][][]){
int timeSteps = states.length;
int agentRows, agentColumns;
@ -459,15 +429,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd index is agent number
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[][], int col){
int rows = states.length;
//int columns = states[0].length;
@ -512,15 +474,7 @@ public class BlockEntropyCalculator extends EntropyCalculator {
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){
int timeSteps = states.length;
//int columns = states[0].length;

View File

@ -21,48 +21,110 @@ package infodynamics.measures.discrete;
import infodynamics.utils.EmpiricalMeasurementDistribution;
/**
* An interface for calculators computing measures from a source to a destination.
* A basic interface for calculators computing measures on a univariate <i>channel</i>
* for discrete (ie int[]) data from a
* source to a destination time-series (ie mutual information and transfer entropy).
* In the following, we refer to the abstract measure computed by this calculator
* as the <i>"channel measure"</i>.
*
* <p>
* Usage of the child classes implementing this interface is intended to follow this paradigm:
* </p>
* <ol>
* <li>Construct the calculator;</li>
* <li>Initialise the calculator using {@link #initialise()} or
* other initialise methods defined by child classes;
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* the set of {@link #addObservations(int[], int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average channel measure: {@link #computeAverageLocalOfObservations()};</li>
* <li>the distribution of channel measure values under the null hypothesis
* of no relationship between source and
* destination values: {@link #computeSignificance(int)};</li>
* <li>or other quantities as defined by child classes.</li>
* </ul>
* </li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* @author Joseph Lizier, jlizier at gmail.com
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public interface ChannelCalculator {
/**
* Initialise the calculator
*
* Initialise the calculator for (re-)use, with the existing
* (or default) values of parameters.
*
* @throws Exception
*/
public void initialise();
/**
* Add observations for the source and destination
* <p>Adds a new set of observations to update the PDFs with.
* It is intended to be called multiple times.
*
* @param states
* @param sourceIndex
* @param destIndex
*/
public void addObservations(int states[][], int sourceIndex, int destIndex);
/**
* Add observations for the source and destination
* @param source
* @param dest
* <p><b>Important:</b> this does not append these observations to the previously
* supplied observations, but treats them independently - i.e. measurements
* such as the transfer entropy will not join them up to examine k
* consecutive values in time.</p>
*
* @param source series of observations for the source variable.
* @param destination series of observations for the destination
* variable. Length must match <code>source</code>, and their indices
* must correspond.
*/
public void addObservations(int[] source, int[] dest);
/**
* Compute the value of the measure
* <p>Adds a new set of observations to update the PDFs with,
* from within a multivariate time-series.
* It is intended to be called multiple times.
*
* @return
* <p><b>Important:</b> this does not append these observations to the previously
* supplied observations, but treats them independently - i.e. measurements
* such as the transfer entropy will not join them up to examine k
* consecutive values in time.</p>
*
* @param states 2D multivariate time series (first index is time
* second indexes the variable)
* @param sourceIndex column index for the source variable.
* @param destIndex column index for the destination variable.
*/
public void addObservations(int states[][], int sourceIndex, int destIndex);
/**
* Compute the channel measure from the previously-supplied samples.
*
* @return the estimate of the channel measure
*/
public double computeAverageLocalOfObservations();
/**
* Compute the significance of the average value for the channel measure here
* Generate a bootstrapped distribution of what the channel measure would look like,
* under a null hypothesis that the source values of our
* samples had no relation to the destination value.
* (Precise null hypothesis varies between MI and TE).
*
* @param numPermutationsToCheck
* @return
* <p>See Section II.E "Statistical significance testing" of
* the JIDT paper below for a description of how this is done for MI,
* conditional MI and TE.
* </p>
*
* <p>Note that if several disjoint time-series have been added
* as observations using {@link #addObservations(int[], int[])} etc.,
* then these separate "trials" will be mixed up in the generation
* of surrogates here.</p>
*
* @param numPermutationsToCheck number of surrogate samples to bootstrap
* to generate the distribution.
* @return the distribution of channel measure scores under this null hypothesis.
* @see "J.T. Lizier, 'JIDT: An information-theoretic
* toolkit for studying the dynamics of complex systems', 2014."
*/
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck);
}

View File

@ -22,18 +22,21 @@ import infodynamics.utils.MathsUtils;
import infodynamics.utils.MatrixUtils;
/**
* A combined calculator for the active information, entropy rate and entropy.
* A combined calculator for the active information,
* entropy rate and entropy.
*
* Usage:
* 1. Continuous accumulation of observations:
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations()
* 2. Standalone:
* Call: localActiveInformation()
* <p>This class is preliminary, so the Javadocs are incomplete --
* please see {@link ActiveInformationCalculator},
* {@link EntropyRateCalculator} and {@link EntropyCalculator}
* for documentation on the corresponding functions
* and typical usage pattern.
* </p>
*
* @author Joseph Lizier
*
* TODO Make this inherit from {@link SingleAgentMeasureInContextOfPastCalculator}
* like {@link ActiveInformationCalculator} and fix the Javadocs
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class CombinedActiveEntRateCalculator {
@ -524,11 +527,4 @@ public class CombinedActiveEntRateCalculator {
public double getLastMinEntropy() {
return minEntropy;
}
// Compute the metrics in several different ways over test data.
// Designed to be called from a JUnit test.
public double[][] test() {
// TODO
return null;
}
}

View File

@ -26,25 +26,48 @@ import infodynamics.utils.EmpiricalMeasurementDistribution;
import infodynamics.utils.RandomGenerator;
/**
* Implements conditional mutual information
* <p>Conditional Mutual information (MI) calculator for univariate discrete (int[]) data.</p>
*
* Usage:
* 1. Continuous accumulation of observations before computing :
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations() or computeAverageLocalOfObservations()
* 2. Standalone computation from a single set of observations:
* Call: computeLocal() or computeAverageLocal()
* <p>Usage of the class is intended to follow this paradigm:</p>
* <ol>
* <li>Construct the calculator:
* {@link #ConditionalMutualInformationCalculator(int, int, int)};</li>
* <li>Initialise the calculator using {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* sets of {@link #addObservations(int[], int[], int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average MI: {@link #computeAverageLocalOfObservations()};</li>
* <li>local MI values, such as
* {@link #computeLocalFromPreviousObservations(int[], int[], int[])};</li>
* <li>comparison to null distribution, such as
* {@link #computeSignificance()};</li>
* <li>and variants of these.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[], int[], int[])}.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* @author Joseph Lizier
* joseph.lizier at gmail.com
* http://lizier.me/joseph/
*
* <p><b>References:</b><br/>
* <ul>
* <li>T. M. Cover and J. A. Thomas, 'Elements of Information
Theory' (John Wiley & Sons, New York, 1991).</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class ConditionalMutualInformationCalculator extends InfoMeasureCalculator implements AnalyticNullDistributionComputer {
public class ConditionalMutualInformationCalculator
extends InfoMeasureCalculator implements AnalyticNullDistributionComputer {
/**
* Store the bases for each variable
* Store the number of symbols for each variable
*/
protected int base1;
protected int base2;
@ -61,16 +84,26 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
* User was formerly forced to create new instances through this factory method.
* Retained for backwards compatibility.
*
* @param base1 base of first MI variable
* @param base2 base of second MI variable
* @param condBase base of conditional variable
*
* @return
* @param base1 number of symbols for first variable.
* E.g. binary variables are in base-2.
* @param base2 number of symbols for second variable.
* @param condBase number of symbols for conditional variable.
* @deprecated As of JIDT 1.0, call {@link #ConditionalMutualInformationCalculator(int, int, int)}
* directly.
* @return new calculator object
*/
public static ConditionalMutualInformationCalculator newInstance(int base1, int base2, int condBase) {
return new ConditionalMutualInformationCalculator(base1, base2, condBase);
}
/**
* Construct a new instance
*
* @param base1 number of symbols for first variable.
* E.g. binary variables are in base-2.
* @param base2 number of symbols for second variable.
* @param condBase number of symbols for conditional variable.
*/
public ConditionalMutualInformationCalculator(int base1, int base2, int condBase) {
// Create super object, just with first base
@ -88,12 +121,7 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
condCount = new int[condBase];
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
*
*/
@Override
public void initialise(){
super.initialise();
@ -106,12 +134,13 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
}
/**
* Add observations for the given var1,var2,cond tuples of the multi-agent system
* Add observations for the given var1,var2,cond tuples
* of the variables
* to our estimates of the pdfs.
*
* @param var1 values for the first variable
* @param var2 values for the second variable
* @param cond values for the conditional variable
* @param var1 series of values for the first variable
* @param var2 series of values for the second variable
* @param cond series of values for the conditional variable
*/
public void addObservations(int var1[], int var2[], int cond[]) {
int rows = var1.length;
@ -129,14 +158,15 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
}
/**
* Add observations for the given var1,var2,cond tuples of the multi-agent system
* Add observations for the given var1,var2,cond tuples
* of the variables
* to our estimates of the pdfs.
* This method signature allows applications using byte data (to save memory)
* to operate correctly.
*
* @param var1 values for the first variable
* @param var2 values for the second variable
* @param cond values for the conditional variable
* @param var1 series of values for the first variable
* @param var2 series of values for the second variable
* @param cond series of values for the conditional variable
*/
public void addObservations(byte var1[], byte var2[], byte cond[]) {
int rows = var1.length;
@ -154,14 +184,16 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
}
/**
* Add observations for the given var1,var2,cond tuples of the multi-agent system
* Add observations for the given var1,var2,cond tuples
* of the variables
* to our estimates of the pdfs, only when those observations are confirmed
* as valid.
*
* @param var1 values for the first variable
* @param var2 values for the second variable
* @param cond values for the conditional variable
* @param valid whether each observation is valid to be counted in the PDFs
* @param var1 series of values for the first variable
* @param var2 series of values for the second variable
* @param cond series of values for the conditional variable
* @param valid series of whether each observation is valid
* to be counted in the PDFs
*/
public void addObservations(int var1[], int var2[], int cond[],
boolean[] valid) {
@ -182,16 +214,18 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
}
/**
* Add observations for the given var1,var2,cond tuples of the multi-agent system
* Add observations for the given var1,var2,cond tuples
* of the variables
* to our estimates of the pdfs, only when those observations are confirmed
* as valid.
* This method signature allows applications using byte data (to save memory)
* to operate correctly.
*
* @param var1 values for the first variable
* @param var2 values for the second variable
* @param cond values for the conditional variable
* @param valid whether each observation is valid to be counted in the PDFs
* @param var1 series of values for the first variable
* @param var2 series of values for the second variable
* @param cond series of values for the conditional variable
* @param valid whether each observation is valid
* to be counted in the PDFs
*/
public void addObservations(byte var1[], byte var2[], byte cond[],
boolean[] valid) {
@ -212,12 +246,19 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
}
/**
* Add observations for the given var1,var2,cond tuples of the multi-agent system
* Add observations for the given var1,var2,cond tuples
* of the variables
* to our estimates of the pdfs.
* This method signature allows multiple trials to have their
* observations added to the PDFs by one method call.
*
* @param var1 values for the first variable
* @param var2 values for the second variable
* @param cond values for the conditional variable
* @param var1 series of values for the first variable;
* first index is time or observation number, second
* index is trial (a second observation number if you will)
* @param var2 series of values for the second variable,
* indexed as per var1
* @param cond series of values for the conditional variable,
* indexed as per var1
*/
public void addObservations(int var1[][], int var2[][], int cond[][]) {
int rows = var1.length;
@ -237,12 +278,7 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
}
}
/**
* Returns the average local conditional MI from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public double computeAverageLocalOfObservations() {
double condMi = 0.0;
double condMiCont = 0.0;
@ -329,10 +365,10 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
* states, using pdfs built up from observations previously
* sent in via the addObservations method.
*
* @param var1 values for the first variable
* @param var2 values for the second variable
* @param cond values for the conditional variable
* @return
* @param var1 series of values for the first variable
* @param var2 series of values for the second variable
* @param cond series of values for the conditional variable
* @return series of local conditional MI values
*/
public double[] computeLocalFromPreviousObservations(int var1[], int var2[], int cond[]){
int rows = var1.length;
@ -367,12 +403,29 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
}
/**
* Compute the significance of obtaining the given average from the given observations,
* assuming that the temporal relationship between variable1 and variable2-conditional
* was destroyed, while variable2-conditional relationship was retained.
* Generate a bootstrapped distribution of what the conditional MI would look like,
* under a null hypothesis that the source values of our
* samples had no relation to the destination value (in the
* context of the conditional).
*
* @param numPermutationsToCheck number of new orderings of the variable1 to compare against
* @return
* <p>See Section II.E "Statistical significance testing" of
* the JIDT paper below for a description of how this is done for MI,
* conditional MI and TE.
* <b>Note that this method currently fixes the relationship
* between variable 2 and the conditional, and shuffles
* variable 1 with respect to these.</b>
* </p>
*
* <p>Note that if several disjoint time-series have been added
* as observations using {@link #addObservations(int[], int[])} etc.,
* then these separate "trials" will be mixed up in the generation
* of surrogates here.</p>
*
* @param numPermutationsToCheck number of surrogate samples to bootstrap
* to generate the distribution.
* @return the distribution of conditional MI scores under this null hypothesis.
* @see "J.T. Lizier, 'JIDT: An information-theoretic
* toolkit for studying the dynamics of complex systems', 2014."
*/
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) {
RandomGenerator rg = new RandomGenerator();
@ -382,16 +435,46 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
}
/**
* Compute the significance of obtaining the given average from the given observations,
* assuming that the temporal relationship between variable1 and variable2-conditional
* was detroyed, while variable2-conditional relationship was retained.
* Generate a bootstrapped distribution of what the conditional MI would look like,
* under a null hypothesis that the source values of our
* samples had no relation to the destination values.
*
* <p>See Section II.E "Statistical significance testing" of
* the JIDT paper below for a description of how this is done for
* a conditional mutual information. Basically, the marginal PDFs
* of each marginal
* are preserved, while their joint PDF is destroyed, and the
* distribution of conditional MI under these conditions is generated.
* <b>Note that this method currently fixes the relationship
* between variable 2 and the conditional, and shuffles
* variable 1 with respect to these.</b>
* </p>
* TODO Need to alter the method signature to allow callers to specify
* which variable is shuffled. (Note to self: when doing this, will
* need to update machine learning code to the new method signature)
*
* @param newOrderings the reorderings for variable1 to use
* @return
* <p>Note that if several disjoint time-series have been added
* as observations using {@link #addObservations(double[])} etc.,
* then these separate "trials" will be mixed up in the generation
* of surrogates here.</p>
*
* <p>This method (in contrast to {@link #computeSignificance(int)})
* allows the user to specify how to construct the surrogates,
* such that repeatable results may be obtained.</p>
*
* @param newOrderings a specification of how to shuffle the values
* of variable 1
* to create the surrogates to generate the distribution with. The first
* index is the permutation number (i.e. newOrderings.length is the number
* of surrogate samples we use to bootstrap to generate the distribution here.)
* Each array newOrderings[i] should be an array of length N (where
* would be the value returned by {@link #getNumObservations()}),
* containing a permutation of the values in 0..(N-1).
* @return the distribution of conditional MI scores under this null hypothesis.
* @see "J.T. Lizier, 'JIDT: An information-theoretic
* toolkit for studying the dynamics of complex systems', 2014."
* @throws Exception where the length of each permutation in newOrderings
* is not equal to the number N samples that were previously supplied.
*/
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) {
@ -456,38 +539,7 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
return measDistribution;
}
/**
* <p>Compute the statistical significance of the conditional mutual information
* result analytically, without creating a distribution
* under the null hypothesis by bootstrapping.</p>
*
* <p>Brillinger (see reference below) showed that under the null hypothesis
* of no source-destination relationship, the MI for two
* discrete distributions follows a chi-square distribution with
* degrees of freedom equal to the product of the number of discrete values
* minus one, for each variable.</p>
*
* <p>Cheng extend this to show that for conditional MI, the number of
* degrees of freedom for this distribution is:
* (base1 - 1)*(base2 - 1)*condBase
* </p>
*
* <p>Barnett and Bossomaier later echoed this for the transfer entropy
* (which is of course a conditional MI)
* </p>
*
* @return ChiSquareMeasurementDistribution object
* This object contains the proportion of conditional MI scores from the distribution
* which have higher or equal conditional MIs to ours.
*
* @see Brillinger, "Some data analyses using mutual information",
* {@link http://www.stat.berkeley.edu/~brill/Papers/MIBJPS.pdf}
* @see Cheng et al., "Data Information in Contingency Tables: A
* Fallacy of Hierarchical Loglinear Models",
* {@link http://www.jds-online.com/file_download/112/JDS-369.pdf}
* @see Barnett and Bossomaier, "Transfer Entropy as a Log-likelihood Ratio"
* {@link http://arxiv.org/abs/1205.6339}
*/
@Override
public AnalyticMeasurementDistribution computeSignificance() {
if (!condMiComputed) {
computeAverageLocalOfObservations();
@ -498,13 +550,13 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
/**
* Standalone routine to
* compute local conditional MI across given variables.
* compute local conditional MI for given variables.
* Return a temporal array of local values.
*
* @param var1 values for the first variable
* @param var2 values for the second variable
* @param cond values for the conditional variable
* @return
* @param var1 series of values for the first variable
* @param var2 series of values for the second variable
* @param cond series of values for the conditional variable
* @return series of local conditional MI values
*/
public double[] computeLocal(int var1[], int var2[], int cond[]) {
@ -515,13 +567,12 @@ public class ConditionalMutualInformationCalculator extends InfoMeasureCalculato
/**
* Standalone routine to
* compute local conditional MI across given variables.
* Returns the average
* compute average conditional MI for a given set of variables.
*
* @param var1 values for the first variable
* @param var2 values for the second variable
* @param cond values for the conditional variable
* @return
* @param var1 series of values for the first variable
* @param var2 series of values for the second variable
* @param cond series of values for the conditional variable
* @return average conditional MI for these values
*/
public double computeAverageLocal(int var1[], int var2[], int cond[]) {

View File

@ -23,18 +23,51 @@ import infodynamics.utils.MatrixUtils;
import infodynamics.utils.EmpiricalMeasurementDistribution;
import infodynamics.utils.RandomGenerator;
/**
* <p>Implements <i>conditional</i> transfer entropy,
* and <i>local conditional</i> transfer entropy
* (see Lizier et al., PRE, 2008, and Lizier et al., Chaos, 2010).
* This class can be used for to compute <i>complete</i> transfer entropy
* (see Lizier et al, PRE, 2008) which conditions on <b>all</b>
* other causal information contributors to the destination.</p>
*
* <p>Specifically, this implements the complete transfer entropy for
* <i>discrete</i>-valued variables.</p>
*
* <p>Implements <b>conditional transfer entropy</b>
* for univariate discrete time-series data.
* That is, it is applied to <code>int[]</code> data, indexed
* by time.
* See Schreiber below for the definition of transfer entropy,
* and Lizier et al (2008, 2010). for the definition of local transfer entropy
* and conditional TE, which is TE conditioned on one or
* more other potential sources.
* This is also called complete TE when all other causal sources
* are conditioned on.
* </p>
*
* <p>
* Usage of the child classes implementing this interface is intended to follow this paradigm:
* </p>
* <ol>
* <li>Construct the calculator via
* {@link #ConditionalTransferEntropyCalculator(int, int, int)};</li>
* <li>Initialise the calculator using
* {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* the set of {@link #addObservations(int[], int[], int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average TE: {@link #computeAverageLocalOfObservations()};</li>
* <li>the local TE values for these samples: {@link #computeLocalOfPreviousObservations()}</li>
* <li>local TE values for a specific set of samples: e.g.
* {@link #computeLocalFromPreviousObservations(int[], int[])} etc.</li>
* <li>the distribution of TE values under the null hypothesis
* of no relationship between source and
* destination values: {@link #computeSignificance(int)} or
* {@link #computeSignificance(int[][])}.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[], int[])} or
* {@link #computeAverageLocal(int[][], int)}.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p>
* The conditional sources (specified using either their
* offsets from the destination variable or their absolute column numbers
@ -43,53 +76,34 @@ import infodynamics.utils.RandomGenerator;
* be incorrect.
* </p>
*
* <p>Ideally, this class would extend ContextOfPastMeasure, however
* <p><i>Note for developers</i>: Ideally, this class would extend ContextOfPastMeasure, however
* by conditioning on other info contributors, we need to alter
* the arrays pastCount and nextPastCount to consider all
* conditioned variables (i.e. other sources) also.
* </p>
*
* <p>Usage:
* <ol>
* <li>Construct: {@link #CompleteTransferEntropyCalculator(int, int)}</li>
* <li>Initialise: {@link #initialise()}</li>
* <li>Either:
* <ol>
* <li>Continuous accumulation of observations then measurement; call:
* <ol>
* <li>{@link #addObservations(int[][], int, int[])} or related calls
* several times over - <b>note:</b> each method call adding
* observations can be viewed as updating the PDFs; they do not
* append the separate time series (this would be incorrect behaviour
* for the transfer entropy, since the start of one time series
* is not necessarily related to the end of the other).</li>
* <li>The compute relevant quantities, e.g.
* {@link #computeLocalFromPreviousObservations(int[][], int, int[])} or
* {@link #computeAverageLocalOfObservations()}</li>
* </ol>
* <li>or Standalone computation from a single set of observations;
* call e.g.: {@link #computeLocal(int[][], int, int[])} or
* {@link #computeAverageLocal(int[][], int, int, int[])}.>/li>
* </ol>
* </ol>
* </p>
* TODO Add methods for passing in single time series.
* This is done for addObservations, but not other routines.
*
* @see "Schreiber, Physical Review Letters 85 (2) pp.461-464, 2000;
* <a href='http://dx.doi.org/10.1103/PhysRevLett.85.461'>download</a>
* (for definition of transfer entropy)"
* @see "Lizier, Prokopenko and Zomaya, Physical Review E 77, 026110, 2008;
* <a href='http://dx.doi.org/10.1103/PhysRevE.77.026110'>download</a>
* (for definition of <i>local</i> transfer entropy and
* <i>complete</i> transfer entropy)"
* @see "Lizier, Prokopenko and Zomaya, Chaos vol. 20, no. 3, 037109, 2010;
* <a href='http://dx.doi.org/10.1063/1.3486801'>download</a>
* (for definition of <i>conditional</i> transfer entropy)"
*
* @author Joseph Lizier, <a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>
* TODO Implement AnalyticNullDistributionComputer
*
* TODO Add methods for passing in single time series
* <p><b>References:</b><br/>
* <ul>
* <li>T. Schreiber, <a href="http://dx.doi.org/10.1103/PhysRevLett.85.461">
* "Measuring information transfer"</a>,
* Physical Review Letters 85 (2) pp.461-464, 2000.</li>
* <li>J. T. Lizier, M. Prokopenko and A. Zomaya,
* <a href="http://dx.doi.org/10.1103/PhysRevE.77.026110">
* "Local information transfer as a spatiotemporal filter for complex systems"</a>
* Physical Review E 77, 026110, 2008.</li>
* <li>J. T. Lizier, M. Prokopenko and A. Zomaya,
* <a href=http://dx.doi.org/10.1063/1.3486801">
* "Information modification and particle collisions in distributed computation"</a>
* Chaos 20, 3, 037109 (2010).</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator {
@ -116,7 +130,7 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
* @param base
* @param history
* @param numOtherInfoContributors
*
* @deprecated
* @return
*/
public static ConditionalTransferEntropyCalculator
@ -138,13 +152,15 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
/**
* Construct a new instance
*
*
* @param base
* @param history
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @param history embedded history length of the destination to condition on -
* this is k in Schreiber's notation.
* @param numOtherInfoContributors number of information contributors
* (other than the past of the destination, if history < 1,
* of the source) to condition on.
* (other than the past of the destination
* or the source) to condition on.
*/
public ConditionalTransferEntropyCalculator
(int base, int history, int numOtherInfoContributors) {
@ -188,12 +204,7 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
*
*/
@Override
public void initialise(){
super.initialise();
@ -204,11 +215,16 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
/**
* Add observations for a single source-destination-conditionals set
* Add observations for source-destination-conditionals time-series
* to our estimates of the pdfs.
*
* @param source source time series
* @param dest destination time series
* @param conditionals conditionals multivariate time series
* @param dest destination time series. Must be of same length as
* source.
* @param conditionals conditionals multivariate time series,
* indexed first by time, then by variable number.
* Must be of same length in time as source, and must be
* {@link #numOtherInfoContributors} conditionals here.
*/
public void addObservations(int[] source, int[] dest, int[][] conditionals)
throws Exception {
@ -272,10 +288,16 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
* {@link MatrixUtils#computeCombinedValues(int[][], int)}. This cannot be
* checked here however, so use at your own risk!
* </p>
*
* @param source source time series
* @param dest destination time series
* @param conditionals conditionals univariate time series - it is assumed
* that the user has combined the values of the multivariate conditionals time series
* @param dest destination time series. Must be of same length as
* source.
* @param conditionals conditionals univariate time series,
* indexed first by time, then by variable number.
* Must be of same length in time as source, and we must have
* either {@link #numOtherInfoContributors}=1, or the user has
* combined the values of the multivariate conditionals time series
* into single (unique) values at each time step (this is not checked however).
*/
public void addObservations(int[] source, int[] dest, int[] conditionals)
throws Exception {
@ -321,22 +343,42 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
/**
* Add observations in to our estimates of the pdfs.
* Add observations in to our estimates of the pdfs
* from a multivariate time-series of homogeneous variables.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs, and all are assumed
* to have other info contributors at same offsets.
*
* @param states
* @param j - number of columns to compute transfer entropy across
* (i.e. src i-j, dest i: transfer is j cells to the right)
* @param otherSourcesToDestOffsets offsets of the other information contributors.
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param j number of columns to compute transfer entropy across
* (i.e. for each destination variable i, we have a source
* at i-j, dest i: transfer is j cells to the right)
* @param otherSourcesToDestOffsets offsets of the other information contributors
* from each destination.
* (i.e. offsets from each other information source to the destination -
* offset is signed the same way as j!)
* the offset is signed the same way as j!)
* othersOffsets is permitted to include j, it will be ignored.
*/
public void addObservations(int states[][], int j, int otherSourcesToDestOffsets[]) {
addObservations(states, j, otherSourcesToDestOffsets, false);
}
/**
* Private method to implement {@link #addObservations(int[][], int, int[])}
*
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param j number of columns to compute transfer entropy across
* (i.e. for each destination variable i, we have a source
* at i-j, dest i: transfer is j cells to the right)
* @param otherSourcesToDestOffsets offsets of the other information contributors
* from each destination.
* (i.e. offsets from each other information source to the destination -
* the offset is signed the same way as j!)
* othersOffsets is permitted to include j, it will be ignored.
* @param cleanedOthers whether it has been checked if j is included in otherSourcesToDestOffsets
* or not
*/
private void addObservations(int states[][], int j, int otherSourcesToDestOffsets[], boolean cleanedOthers) {
int[] cleanedOthersOffsets;
@ -392,16 +434,36 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
/**
* Add observations for a single source-destination pair of the multi-agent system
* Add observations for a single source-destination-conditionals set of the
* multivariate time series.
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param sourceCol column index of the source
* @param destCol column index of the destination
* @param othersAbsolute column indices of the conditional variables.
* othersAbsolute is permitted to include sourceCol or destCol (if k>0),
* they will be ignored.
*/
public void addObservations(int states[][], int sourceCol, int destCol, int[] othersAbsolute) {
addObservations(states, sourceCol, destCol, othersAbsolute, false);
}
/**
* Private method to implement {@link #addObservations(int[][], int, int, int[])}
*
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param sourceCol column index of the source
* @param destCol column index of the destination
* @param othersAbsolute column indices of the conditional variables.
* othersAbsolute is permitted to include sourceCol or destCol (if k>0),
* they will be ignored.
* @param cleanedOthers whether it has been checked if othersAbsolute
* contains sourceCol or destCol
*/
private void addObservations(int states[][], int sourceCol, int destCol, int[] othersAbsolute, boolean cleanedOthers) {
int[] cleanedOthersAbsolute;
@ -451,12 +513,7 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
}
/**
* Returns the average local transfer entropy from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public double computeAverageLocalOfObservations() {
double te = 0.0;
double teCont = 0.0;
@ -508,14 +565,27 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
/**
* Compute the significance of obtaining the given average TE from the given observations
* Generate a bootstrapped distribution of what the
* conditional TE would look like,
* under a null hypothesis that the source values of our
* samples had no relation to the destination value
* (in the context of the destination past and conditionals).
*
* This is as per Chavez et. al., "Statistical assessment of nonlinear causality:
* application to epileptic EEG signals", Journal of Neuroscience Methods 124 (2003) 113-128.
* except that we've using conditional/complete TE here.
*
* @param numPermutationsToCheck number of new orderings of the source values to compare against
* @return
* <p>See Section II.E "Statistical significance testing" of
* the JIDT paper below for a description of how this is done for MI,
* conditional MI and TE.
* </p>
*
* <p>Note that if several disjoint time-series have been added
* as observations using {@link #addObservations(int[], int[])} etc.,
* then these separate "trials" will be mixed up in the generation
* of surrogates here.</p>
*
* @param numPermutationsToCheck number of surrogate samples to bootstrap
* to generate the distribution.
* @return the distribution of conditional TE scores under this null hypothesis.
* @see "J.T. Lizier, 'JIDT: An information-theoretic
* toolkit for studying the dynamics of complex systems', 2014."
*/
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) {
double actualTE = computeAverageLocalOfObservations();
@ -604,26 +674,47 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
/**
* Computes local complete transfer entropy for the given
* Computes local conditional transfer entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method.
* This method to be used for homogeneous agents only
*
* @param states 2D multivariate time series of states
* @param j - number of columns to compute transfer entropy across
* i.e. offset of destination from the source
* (i.e. src i-j, dest i: transfer is j cells to the right)
* @param otherSourcesToDestOffsets offsets of the other information contributors.
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param j number of columns to compute transfer entropy across
* (i.e. for each destination variable i, we have a source
* at i-j, dest i: transfer is j cells to the right)
* @param otherSourcesToDestOffsets offsets of the other information contributors
* from each destination.
* (i.e. offsets from each other information source to the destination -
* offset is signed the same way as j!)
* the offset is signed the same way as j!)
* othersOffsets is permitted to include j, it will be ignored.
* @return
* @return 2D time-series of local conditional TE values (indexed
* as per states)
*/
public double[][] computeLocalFromPreviousObservations
(int states[][], int j, int otherSourcesToDestOffsets[]){
return computeLocalFromPreviousObservations(states, j, otherSourcesToDestOffsets, false);
}
/**
* Private method to implement {@link #computeLocalFromPreviousObservations(int[][], int, int[])}
*
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param j number of columns to compute transfer entropy across
* (i.e. for each destination variable i, we have a source
* at i-j, dest i: transfer is j cells to the right)
* @param otherSourcesToDestOffsets offsets of the other information contributors
* from each destination.
* (i.e. offsets from each other information source to the destination -
* the offset is signed the same way as j!)
* othersOffsets is permitted to include j, it will be ignored.
* @param cleanedOthers whether it has been checked if j is included in otherSourcesToDestOffsets
* or not
* @return 2D time-series of local conditional TE values (indexed
* as per states)
*/
private double[][] computeLocalFromPreviousObservations
(int states[][], int j, int othersOffsets[], boolean cleanedOthers){
@ -697,14 +788,34 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
* sent in via the addObservations method.
* This method is suitable for heterogeneous agents
*
* @param states
* @return
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param sourceCol column index of the source
* @param destCol column index of the destination
* @param othersAbsolute column indices of the conditional variables.
* othersAbsolute is permitted to include sourceCol or destCol (if k>0),
* they will be ignored.
* @return time-series of local conditional TE values
*/
public double[] computeLocalFromPreviousObservations
(int states[][], int sourceCol, int destCol, int[] othersAbsolute){
return computeLocalFromPreviousObservations(states, sourceCol, destCol, othersAbsolute, false);
}
/**
* Private method to implement {@link #computeLocalFromPreviousObservations(int[][], int, int, int[])}
*
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param sourceCol column index of the source
* @param destCol column index of the destination
* @param othersAbsolute column indices of the conditional variables.
* othersAbsolute is permitted to include sourceCol or destCol (if k>0),
* they will be ignored.
* @param cleanedOthers whether it has been checked if othersAbsolute
* contains sourceCol or destCol
* @return time-series of local conditional TE values
*/
private double[] computeLocalFromPreviousObservations
(int states[][], int sourceCol, int destCol, int[] othersAbsolute, boolean cleanedOthers){
@ -770,17 +881,24 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
/**
* Standalone routine to
* compute local transfer entropy across a 2D spatiotemporal
* compute local conditional transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method to be called for homogeneous agents only
*
* @param states - 2D array of states
* @param j - TE across j cells to the right
* @param otherSourcesToDestOffsets - column offsets from other causal info contributors
* to the destination
* @return
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param j number of columns to compute transfer entropy across
* (i.e. for each destination variable i, we have a source
* at i-j, dest i: transfer is j cells to the right)
* @param otherSourcesToDestOffsets offsets of the other information contributors
* from each destination.
* (i.e. offsets from each other information source to the destination -
* the offset is signed the same way as j!)
* othersOffsets is permitted to include j, it will be ignored.
* @return 2D time-series of local conditional TE values (indexed
* as per states)
*/
public double[][] computeLocal(int states[][], int j, int[] otherSourcesToDestOffsets) {
@ -792,16 +910,22 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
/**
* Standalone routine to
* compute average local transfer entropy across a 2D spatiotemporal
* compute average conditional transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* This method to be called for homogeneous agents only
*
* @param states - 2D array of states
* @param j - TE across j cells to the right
* @param otherSourcesToDestOffsets - column offsets from other causal info contributors
* to the destination
* @return
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param j number of columns to compute transfer entropy across
* (i.e. for each destination variable i, we have a source
* at i-j, dest i: transfer is j cells to the right)
* @param otherSourcesToDestOffsets offsets of the other information contributors
* from each destination.
* (i.e. offsets from each other information source to the destination -
* the offset is signed the same way as j!)
* othersOffsets is permitted to include j, it will be ignored.
* @return average conditional TE from these observations
*/
public double computeAverageLocal(int states[][], int j, int[] otherSourcesToDestOffsets) {
@ -812,17 +936,21 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
/**
* Standalone routine to
* compute local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* compute local conditional transfer entropy for a specific set of
* source-destination-conditionals in a multivariate time-series.
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method suitable for heterogeneous agents
* First history rows are zeros.
* This method suitable for heterogeneous agents.
*
* @param states - 2D array of states
* @param sourceCol - column index for the source agent
* @param destCol - column index for the destination agent
* @param othersAbsolute - column indices for other causal info contributors
* @return
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param sourceCol column index of the source
* @param destCol column index of the destination
* @param othersAbsolute column indices of the conditional variables.
* othersAbsolute is permitted to include sourceCol or destCol (if k>0),
* they will be ignored.
* @return time-series of local conditional TE values for these
* observations
*/
public double[] computeLocal(int states[][], int sourceCol, int destCol, int[] othersAbsolute) {
@ -835,16 +963,18 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
/**
* Standalone routine to
* compute average local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* Returns the average
* compute average conditional transfer entropy for a specific set of
* source-destination-conditionals in a multivariate time-series.
* This method suitable for heterogeneous agents
*
* @param states - 2D array of states
* @param sourceCol - column index for the source agent
* @param destCol - column index for the destination agent
* @param othersAbsolute - column indices for other causal info contributors
* @return
* @param states multivariate time series, indexed first by time
* then by variable number.
* @param sourceCol column index of the source
* @param destCol column index of the destination
* @param othersAbsolute column indices of the conditional variables.
* othersAbsolute is permitted to include sourceCol or destCol (if k>0),
* they will be ignored.
* @return average conditional transfer entropy for these observations
*/
public double computeAverageLocal(int states[][], int sourceCol, int destCol, int[] othersAbsolute) {
@ -854,15 +984,19 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
}
/**
* Counts the information contributors to this node which
* are not equal to the source to dest offset j or the node itself (offset 0,
* node itself not included only when removeDest is set to true)
* Counts the unique information contributors to this node which
* are not equal to the source at offset j from the destination,
* or the node itself (offset 0,
* node itself not included only when removeDest is set to true).
*
* <p>This is primarily intended for use inside the method, but
* made public as a utility.</p>
*
* @param otherSourcesToDestOffsets array of offsets of the destination from each source
* @param j offset of the destination from the source
* @param removeDest remove the destination itself from the count
* of offset others.
* @return
* @return the number of unique such sources
*/
public static int countOfOffsetOthers(int[] otherSourcesToDestOffsets, int j,
boolean removeDest) {
@ -881,12 +1015,15 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
* are not equal to src or the node itself (offset 0,
* node itself not included only when removeDest is set to true)
*
* @param others
* @param src
* @param dest
* <p>This is primarily intended for use inside the method, but
* made public as a utility.</p>
*
* @param others array of source indices
* @param src current source index we are considering
* @param dest current dest index we are considering
* @param removeDest remove the destination itself from the count
* of absolute others.
* @return
* @return the number of unique such sources
*/
public static int countOfAbsoluteOthers(int[] others, int src, int dest,
boolean removeDest) {
@ -908,7 +1045,8 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
* @param j offset from the source to the destination
* @param removeDest remove the destination itself from the count
* of absolute others.
* @return
* @return whether the number of such sources matches what we expect
* @throws Exception if the number of such sources does not match.
*/
public boolean confirmEnoughOffsetOthers(int[] othersOffsets, int j,
boolean removeDest) {
@ -923,12 +1061,13 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
* Check that the supplied array of absolutes as other info
* contributors is long enough compared to our expectation
*
* @param othersAbsolute
* @param src
* @param dest
* @param othersAbsolute array of source indices
* @param src current source index we are considering
* @param dest current dest index we are considering
* @param removeDest remove the destination itself from the count
* of absolute others.
* @return
* @return whether the number of such sources matches what we expect
* @throws Exception if the number of such sources does not match.
*/
public boolean confirmEnoughAbsoluteOthers(int[] othersAbsolute, int src,
int dest, boolean removeDest) {
@ -951,7 +1090,8 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
* other sources (if it is there). Should not be done
* if k == 0 (because then the destination is not included
* in the past history)
* @return
* @return othersOffsets with entries for the offending
* contributors removed (i.e. array may be shortened)
*/
public int[] cleanOffsetOthers(int[] othersOffsets, int j, boolean removeDest) {
int[] cleaned = new int[numOtherInfoContributors];
@ -984,14 +1124,15 @@ public class ConditionalTransferEntropyCalculator extends InfoMeasureCalculator
* removed only if removeDest is true).
* Checks that there are enough other information contributors.
*
* @param others
* @param src
* @param dest
* @param others array of source indices
* @param src current source index we are considering
* @param dest current dest index we are considering
* @param removeDest Remove the destination itself from the cleaned
* other sources (if it is there). Should not be done
* if k == 0 (because then the destination is not included
* in the past history)
* @return
* @return others with entries for the offending
* contributors removed (i.e. array may be shortened)
*/
public int[] cleanAbsoluteOthers(int[] others, int src, int dest,
boolean removeDest) {

View File

@ -22,38 +22,55 @@ import infodynamics.utils.MathsUtils;
import infodynamics.utils.MatrixUtils;
/**
* @author Joseph Lizier
* A base class for calculators computing measures which
* require knowledge of the embedded past state of a univariate
* discrete (ie int[]) variable.
*
* Info theoretic measure calculator base class for
* measures which require the context of the past
* history of the destination variable.
* Usage is as per {@link InfoMeasureCalculator}, but with some
* extra utility functions provided for computing embedding vectors.
*
* Usage:
* 1. Continuous accumulation of observations before computing :
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations() or computeAverageLocalOfObservations()
* 2. Standalone computation from a single set of observations:
* Call: computeLocal() or computeAverageLocal()
*
* @author Joseph Lizier
* joseph.lizier at gmail.com
* http://lizier.me/joseph/
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public abstract class ContextOfPastMeasureCalculator extends
InfoMeasureCalculator {
protected int k = 0; // history length k.
/**
* History length for the embedding
*/
protected int k = 0;
/**
* Do not create storage
* for observations of the embedded past
*/
protected boolean noObservationStorage = false;
/**
* Counts of (next,embedded_past) tuples
*/
protected int[][] nextPastCount = null; // Count for (i[t+1], i[t]) tuples
/**
* Counts of (embedded_past) tuples
*/
protected int[] pastCount = null; // Count for i[t]
/**
* Counts of (next) observations
*/
protected int[] nextCount = null; // count for i[t+1]
protected int[] maxShiftedValue = null; // states * (base^(k-1))
/**
* Cached value maxShiftedValue[i] is i * (base^(k-1))
*/
protected int[] maxShiftedValue = null; //
/**
* Cached value of base^k
*/
protected int base_power_k = 0;
/**
* @param base
* Construct an instance
*
* @param base number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
* @param history embedding length
*/
public ContextOfPastMeasureCalculator(int base, int history) {
this(base, history, false);
@ -64,9 +81,12 @@ public abstract class ContextOfPastMeasureCalculator extends
* In general, only needs to be explicitly called if child classes
* do not wish to create the observation arrays.
*
* @param base
* @param history
* @param dontCreateObsStorage
* @param base number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
* @param history embedding length
* @param dontCreateObsStorage do not create storage
* for observations of the embedded past (as the child
* class is signalling that it does not need it)
*/
protected ContextOfPastMeasureCalculator(int base, int history, boolean dontCreateObsStorage) {
super(base);
@ -100,12 +120,7 @@ public abstract class ContextOfPastMeasureCalculator extends
}
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
*
*/
@Override
public void initialise() {
super.initialise();
@ -117,12 +132,15 @@ public abstract class ContextOfPastMeasureCalculator extends
}
/**
* Utility function to compute the combined past values of x up to and including time step t
* Utility function to compute the combined embedded
* past values of x up to and including time step t
* (i.e. (x_{t-k+1}, ... ,x_{t-1},x_{t}))
*
* @param x
* @param t
* @return
* @param x time-series data
* @param t compute embedding vector up to and
* including index t
* @return int value representing the embedding vector
* translated into a unique integer
*/
public int computePastValue(int[] x, int t) {
int pastVal = 0;
@ -134,37 +152,50 @@ public abstract class ContextOfPastMeasureCalculator extends
}
/**
* Utility function to compute the combined past values of x up to and including time step t
* Utility function to compute the combined embedded
* past values of x up to and including time step t
* (i.e. (x_{t-k+1}, ... ,x_{t-1},x_{t}))
* where x is a column in data
*
* @param x
* @param agentNumber
* @param t
* @return
* @param data multivariate time-series data
* (first index is time, second is variable number)
* @param columnNumber which column to embed
* @param t compute embedding vector up to and
* including index t
* @return int value representing the embedding vector
* translated into a unique integer
*/
public int computePastValue(int[][] x, int agentNumber, int t) {
public int computePastValue(int[][] data, int columnNumber, int t) {
int pastVal = 0;
for (int p = 0; p < k; p++) {
pastVal *= base;
pastVal += x[t - k + 1 + p][agentNumber];
pastVal += data[t - k + 1 + p][columnNumber];
}
return pastVal;
}
/**
* Utility function to compute the combined past values of x up to and including time step t
* Utility function to compute the combined embedded
* past values of x up to and including time step t
* (i.e. (x_{t-k+1}, ... ,x_{t-1},x_{t}))
* where x is a time-series for a given row and
* column in data
*
* @param x
* @param agentNumber
* @param t
* @return
* @param data multivariate time-series data
* (first index is time, second is row number for the variable
* and third is column number for the variable)
* @param rowNumber row number of the variable to embed
* @param columnNumber column number of the variable to embed
* @param t compute embedding vector up to and
* including index t
* @return int value representing the embedding vector
* translated into a unique integer
*/
public int computePastValue(int[][][] x, int agentRow, int agentColumn, int t) {
public int computePastValue(int[][][] data, int rowNumber, int columnNumber, int t) {
int pastVal = 0;
for (int p = 0; p < k; p++) {
pastVal *= base;
pastVal += x[t - k + 1 + p][agentRow][agentColumn];
pastVal += data[t - k + 1 + p][rowNumber][columnNumber];
}
return pastVal;
}

View File

@ -21,30 +21,39 @@ package infodynamics.measures.discrete;
import infodynamics.utils.MatrixUtils;
/**
* <p>Univariate entropy calculator</p>
* <p>Entropy calculator for univariate discrete (int[]) data.</p>
*
* <p>Usage:
* <p>Usage of the class is intended to follow this paradigm:</p>
* <ol>
* <li>Continuous accumulation of observations. Call:
* <ol>
* <li>{@link #initialise()};</li>
* <li>then supply the observations using any of the addObservations()
* methods (several times over), e.g. {@link #addObservations(int[][])};</li>
* <li>then when all observations have been added, call any of the
* compute methods (several times over), e.g.:
* {@link #computeLocalFromPreviousObservations(int[][])}</li>
* </ol>
* </li>
* <li>Standalone mode. Call:
* <ol>
* <li>one of the standalone methods which supply observations
* and compute at once, e.g. {@link #computeAverageLocal(int[][])}.</li>
* </ol>
* </li>
* </ol></p>
* <li>Construct the calculator: {@link #EntropyCalculator(int)};</li>
* <li>Initialise the calculator using {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* sets of {@link #addObservations(int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average entropy: {@link #computeAverageLocalOfObservations()};</li>
* <li>local entropy values, such as {@link #computeLocal(int[])};</li>
* <li>and variants of these.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[])},
* {@link #computeAverageLocal(int[])} etc.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p><b>References:</b><br/>
* <ul>
* <li>T. M. Cover and J. A. Thomas, 'Elements of Information
Theory' (John Wiley & Sons, New York, 1991).</li>
* </ul>
*
* @author Joseph Lizier
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class EntropyCalculator extends InfoMeasureCalculator
implements SingleAgentMeasure
@ -56,10 +65,12 @@ public class EntropyCalculator extends InfoMeasureCalculator
* User was formerly forced to create new instances through this factory method.
* Retained for backwards compatibility.
*
* @param base
* @param blocksize
*
* @return
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @param blocksize number of consecutive joint values to include
* in the calculation.
* @deprecated
* @return a new EntropyCalculator
*/
public static EntropyCalculator newInstance(int base, int blocksize) {
if (blocksize > 1) {
@ -73,8 +84,10 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
/**
* Contruct a new instance
*
* @param base
* @param base number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
*/
public EntropyCalculator(int base) {
@ -84,22 +97,13 @@ public class EntropyCalculator extends InfoMeasureCalculator
stateCount = new int[base];
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
*/
@Override
public void initialise(){
super.initialise();
MatrixUtils.fill(stateCount, 0);
}
/**
* Add observations in to our estimates of the pdfs.
*
* @param states 1st index is time
*/
@Override
public void addObservations(int states[]) {
int rows = states.length;
// increment the count of observations:
@ -112,13 +116,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
}
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd index is agent number
*/
@Override
public void addObservations(int states[][]) {
int rows = states.length;
int columns = states[0].length;
@ -134,13 +132,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
}
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
*/
@Override
public void addObservations(int states[][][]) {
int timeSteps = states.length;
if (timeSteps == 0) {
@ -165,15 +157,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
}
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd index is agent number
* @param agentNumber
*/
@Override
public void addObservations(int states[][], int agentNumber) {
int rows = states.length;
// increment the count of observations:
@ -186,16 +170,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
}
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param agentIndex1
* @param agentIndex2
*/
@Override
public void addObservations(int states[][][], int agentIndex1, int agentIndex2) {
int timeSteps = states.length;
// increment the count of observations:
@ -209,8 +184,9 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
/**
* Return the current count for the given value
*
* @param stateVal
* @param stateVal given value
* @return count of observations of the given state
*/
public int getStateCount(int stateVal) {
@ -218,20 +194,16 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
/**
* Return the current probability for the given value
*
* @param stateVal
* @param stateVal given value
* @return probability of the given state
*/
public double getStateProbability(int stateVal) {
return (double) stateCount[stateVal] / (double) observations;
}
/**
* Returns the average entropy from
* the observed values which have been passed in previously.
*
* @return the average entropy
*/
@Override
public double computeAverageLocalOfObservations() {
double ent = 0.0;
double entCont = 0.0;
@ -260,14 +232,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
return ent;
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
*
* @param states index is time
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[]){
int rows = states.length;
@ -292,15 +257,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd index is agent number
* @return
*/
@Override
public double[][] computeLocalFromPreviousObservations(int states[][]){
int rows = states.length;
int columns = states[0].length;
@ -328,15 +285,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @return
*/
@Override
public double[][][] computeLocalFromPreviousObservations(int states[][][]){
int timeSteps = states.length;
int agentRows, agentColumns;
@ -377,16 +326,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
*
* @param states
* @param agentNumber
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[][], int agentNumber){
int rows = states.length;
//int columns = states[0].length;
@ -412,17 +352,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
/**
* Computes local entropy for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @param agentIndex1
* @param agentIndex2
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){
int timeSteps = states.length;
//int columns = states[0].length;
@ -449,16 +379,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
}
/**
* Standalone routine to
* compute local information theoretic measure across a 1D temporal
* array of the states of homogeneous agents
* Return a 1D temporal array of local values.
* First history rows are zeros
*
* @param states - 1D array of states
* @return
*/
@Override
public final double[] computeLocal(int states[]) {
initialise();
@ -466,16 +387,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
return computeLocalFromPreviousObservations(states);
}
/**
* Standalone routine to
* compute local information theoretic measure across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
*
* @param states - 2D array of states
* @return
*/
@Override
public final double[][] computeLocal(int states[][]) {
initialise();
@ -483,16 +395,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
return computeLocalFromPreviousObservations(states);
}
/**
* Standalone routine to
* compute local information theoretic measure across a 3D spatiotemporal
* array of the states of 2D homogeneous agents
* Return a 3D spatiotemporal array of local values.
* First history rows are zeros
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @return
*/
@Override
public final double[][][] computeLocal(int states[][][]) {
initialise();
@ -500,15 +403,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
return computeLocalFromPreviousObservations(states);
}
/**
* Standalone routine to
* compute average local information theoretic measure across a 1D temporal
* array of the states of homogeneous agents
* Return the average
*
* @param states - 1D array of states
* @return
*/
@Override
public final double computeAverageLocal(int states[]) {
initialise();
@ -516,16 +411,7 @@ public class EntropyCalculator extends InfoMeasureCalculator
return computeAverageLocalOfObservations();
}
/**
* Standalone routine to
* compute average local information theoretic measure across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* This method to be called for homogeneous agents only
*
* @param states - 2D array of states
* @return
*/
@Override
public final double computeAverageLocal(int states[][]) {
initialise();
@ -533,92 +419,37 @@ public class EntropyCalculator extends InfoMeasureCalculator
return computeAverageLocalOfObservations();
}
/**
* Standalone routine to
* compute average local information theoretic measure across a 3D spatiotemporal
* array of the states of 2D homogeneous agents
* Return the average
* This method to be called for homogeneous agents only
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @return
*/
@Override
public final double computeAverageLocal(int states[][][]) {
initialise();
addObservations(states);
return computeAverageLocalOfObservations();
}
/**
* Standalone routine to
* compute local information theoretic measure for one agent in a 2D spatiotemporal
* array of the states of agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method should be used for heterogeneous agents
*
* @param states - 2D array of states
* @param col - column number of the agent in the states array
* @return
*/
public final double[] computeLocalAtAgent(int states[][], int col) {
@Override
public final double[] computeLocal(int states[][], int col) {
initialise();
addObservations(states, col);
return computeLocalFromPreviousObservations(states, col);
}
/**
* Standalone routine to
* compute local information theoretic measure for one agent in a 2D spatiotemporal
* array of the states of agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method should be used for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @param agentIndex1
* @param agentIndex2
* @return
*/
public final double[] computeLocalAtAgent(int states[][][], int agentIndex1, int agentIndex2) {
@Override
public final double[] computeLocal(int states[][][],
int agentIndex1, int agentIndex2) {
initialise();
addObservations(states, agentIndex1, agentIndex2);
return computeLocalFromPreviousObservations(states, agentIndex1, agentIndex2);
}
/**
* Standalone routine to
* compute average local information theoretic measure
* for a single agent
* Returns the average
* This method suitable for heterogeneous agents
* @param states - 2D array of states
* @param col - column number of the agent in the states array
*
* @return
*/
public final double computeAverageLocalAtAgent(int states[][], int col) {
@Override
public final double computeAverageLocal(int states[][], int col) {
initialise();
addObservations(states, col);
return computeAverageLocalOfObservations();
}
/**
* Standalone routine to
* compute average local information theoretic measure
* for a single agent
* Returns the average
* This method suitable for heterogeneous agents
* @param states - 2D array of states
* @param agentIndex1
* @param agentIndex2
*
* @return
*/
public final double computeAverageLocalAtAgent(int states[][][], int agentIndex1, int agentIndex2) {
@Override
public final double computeAverageLocal(int states[][][], int agentIndex1, int agentIndex2) {
initialise();
addObservations(states, agentIndex1, agentIndex2);
return computeAverageLocalOfObservations();

View File

@ -19,18 +19,48 @@
package infodynamics.measures.discrete;
/**
* Compute average and local entropy rates
*
* Usage:
* 1. Continuous accumulation of observations:
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations()
* 2. Standalone:
* Call: localActiveInformation()
*
* @author Joseph Lizier
* <p>Entropy rate calculator for univariate discrete (int[]) data
* (ie computes entropy over blocks of consecutive states in time).
* Implements entropy rate as entropy of next state
* conditional on the embedded past (as per the alternative
* definition used by Crutchfield and Feldman, see below)
* rather than the limiting rate of block entropy over block size.</p>
*
* <p>Usage of the class is intended to follow this paradigm:</p>
* <ol>
* <li>Construct the calculator: {@link #EntropyRateCalculator(int, int)};</li>
* <li>Initialise the calculator using {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* sets of {@link #addObservations(int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average entropy: {@link #computeAverageLocalOfObservations()};</li>
* <li>local entropy values, such as {@link #computeLocal(int[])};</li>
* <li>and variants of these.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[])},
* {@link #computeAverageLocal(int[])} etc.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p><b>References:</b><br/>
* <ul>
* <li>T. M. Cover and J. A. Thomas, 'Elements of Information
Theory' (John Wiley & Sons, New York, 1991).</li>
* <li>J. P. Crutchfield, D. P. Feldman,
* <a href="http://dx.doi.org/10.1063/1.1530990">
* "Regularities Unseen, Randomness Observed: Levels of Entropy Convergence"</a>,
* Chaos, Vol. 13, No. 1. (2003), pp. 25-54.</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalculator {
@ -40,26 +70,54 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
*
* @param base
* @param history
*
* @deprecated
* @return
*/
public static EntropyRateCalculator newInstance(int base, int history) {
return new EntropyRateCalculator(base, history);
}
/**
* Construct a new instance
*
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @param history embedded history length of the destination to condition on -
* this is k in Schreiber's notation.
*/
public EntropyRateCalculator(int base, int history) {
super(base, history);
}
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd index is agent number
*/
@Override
public void addObservations(int[] states) {
int rows = states.length;
// increment the count of observations:
observations += (rows - k);
// Initialise and store the current previous value for each column
int prevVal = 0;
for (int p = 0; p < k; p++) {
prevVal *= base;
prevVal += states[p];
}
// 1. Count the tuples observed
int nextVal;
for (int r = k; r < rows; r++) {
// Add to the count for this particular transition:
// (cell's assigned as above)
nextVal = states[r];
nextPastCount[nextVal][prevVal]++;
pastCount[prevVal]++;
// Update the previous value:
prevVal -= maxShiftedValue[states[r-k]];
prevVal *= base;
prevVal += states[r];
}
}
@Override
public void addObservations(int states[][]) {
int rows = states.length;
int columns = states[0].length;
@ -93,13 +151,7 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
}
}
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
*/
@Override
public void addObservations(int states[][][]) {
int timeSteps = states.length;
if (timeSteps == 0) {
@ -144,14 +196,7 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
}
}
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd index is agent number
*/
@Override
public void addObservations(int states[][], int col) {
int rows = states.length;
// increment the count of observations:
@ -180,14 +225,7 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
}
}
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
*/
@Override
public void addObservations(int states[][][], int agentIndex1, int agentIndex2) {
int timeSteps = states.length;
// increment the count of observations:
@ -216,12 +254,7 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
}
}
/**
* Returns the average local active information storage from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public double computeAverageLocalOfObservations() {
double entRate = 0.0;
double entRateCont = 0.0;
@ -257,15 +290,48 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
return entRate;
}
/**
* Computes local entropy rate for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd index is agent number
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int[] states) {
int rows = states.length;
// Allocate for all rows even though we'll leave the first ones as zeros
double[] localEntRate = new double[rows];
average = 0;
max = 0;
min = 0;
// Initialise and store the current previous value for each column
int prevVal = 0;
for (int p = 0; p < k; p++) {
prevVal *= base;
prevVal += states[p];
}
int nextVal;
double logTerm = 0.0;
for (int r = k; r < rows; r++) {
nextVal = states[r];
logTerm = ( (double) nextPastCount[nextVal][prevVal] ) /
( (double) pastCount[prevVal] );
// Entropy rate takes the negative log:
localEntRate[r] = - Math.log(logTerm) / log_2;
average += localEntRate[r];
if (localEntRate[r] > max) {
max = localEntRate[r];
} else if (localEntRate[r] < min) {
min = localEntRate[r];
}
// Update the previous value:
prevVal -= maxShiftedValue[states[r-k]];
prevVal *= base;
prevVal += states[r];
}
average = average/(double) (rows - k);
return localEntRate;
}
@Override
public double[][] computeLocalFromPreviousObservations(int states[][]){
int rows = states.length;
int columns = states[0].length;
@ -308,19 +374,10 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
}
average = average/(double) (columns * (rows - k));
return localEntRate;
return localEntRate;
}
/**
* Computes local entropy rate for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @return
*/
@Override
public double[][][] computeLocalFromPreviousObservations(int states[][][]){
int timeSteps = states.length;
int agentRows = states[0].length;
@ -370,18 +427,9 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
average = average/(double) (agentRows * agentColumns * (timeSteps - k));
return localEntRate;
}
/**
* Computes local entropy rate for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd index is agent number
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[][], int col){
int rows = states.length;
//int columns = states[0].length;
@ -424,15 +472,7 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
}
/**
* Computes local entropy rate for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @return
*/
@Override
public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){
int timeSteps = states.length;
//int columns = states[0].length;
@ -473,5 +513,5 @@ public class EntropyRateCalculator extends SingleAgentMeasureInContextOfPastCalc
return localEntRate;
}
}
}

View File

@ -19,57 +19,89 @@
package infodynamics.measures.discrete;
/**
* <p>Info theoretic measure calculator base class, providing common functionality
* <p>Base class for our information-theoretic calculators
* on discrete (int[]) data,
* providing common functionality
* for user-level measure classes.</p>
*
* <p>Usage of child classes is intended to follow this general pattern:
* <p>
* Usage of the child classes extending this class is intended to follow this paradigm:
* </p>
* <ol>
* <li>Construct;</li>
* <li>{@link #initialise()};</li>
* <li>Then, either of the following:
* <ol>
* <li>Continuous accumulation of observations before computing, via:
* <ol>
* <li>calling "addObservations()" methods of children
* several times over;</li>
* <li>Compute required quantities, using
* {@link #computeAverageLocalOfObservations()} or
* "computeLocalUsinPreviousObservations()".</li>
* </ol></li>
* <li>Standalone computation from a single set of observations; call:
* "computeLocal()" or "computeAverageLocal()".</li>
* </ol>
* </ol></p>
* <li>Construct the calculator;</li>
* <li>Initialise the calculator using {@link #initialise()} or
* other initialise methods defined by child classes;</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* sets of "addObservations" methods defined by child classes, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average measure: {@link #computeAverageLocalOfObservations()};</li>
* <li>or other quantities as defined by child classes.</li>
* </ul>
* </li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p>Note: various functions referred to above (e.g. "addObservations()")
* are not specified here, so that this class can be a superclass
* for both univariate, pairwise and multivariate methods.</p>
*
* @author Joseph Lizier
* joseph.lizier at gmail.com
* http://lizier.me/joseph/
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public abstract class InfoMeasureCalculator {
/**
* Last computed average of the measure
*/
protected double average = 0.0;
/**
* Last computed max local value of the measure
*/
protected double max = 0.0;
/**
* Last computed min local value of the measure
*/
protected double min = 0.0;
/**
* Last computed standard deviation of local values of the measure
*/
protected double std = 0.0;
/**
* Number of observations supplied for the PDFs
*/
protected int observations = 0;
/**
* Number of available quantised states for each variable
* (ie binary is base-2).
*/
protected int base = 0; // number of individual states. Need initialised to 0 for changedSizes
/**
* Cached value of ln(base)
*/
protected double log_base = 0;
/**
* Cached value of ln(2)
*/
protected double log_2 = Math.log(2.0);
/**
* Cache of whether the base is a power of 2
*/
protected boolean power_of_2_base = false;
/**
* Cached value of log_2(base)
*/
protected int log_2_base = 0;
/**
* Whether we're in debug mode
*/
protected boolean debug = false;
/**
* Construct an instance
*
* @param blocksize
* @param base
* @param base number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
*/
protected InfoMeasureCalculator(int base) {
@ -88,9 +120,8 @@ public abstract class InfoMeasureCalculator {
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
* Initialise the calculator for re-use with new observations.
* (Child classes should clear the existing PDFs)
*/
public void initialise(){
average = 0.0;
@ -100,38 +131,66 @@ public abstract class InfoMeasureCalculator {
observations = 0;
}
/**
* Return the measure last calculated in a call to
* {@link #computeAverageLocalOfObservations()}
* or related methods after the previous
* {@link #initialise()} call.
*
* @return the last computed measure value
*/
public final double getLastAverage() {
return average;
}
/**
* Return the last computed max local value of the measure.
* Not declaring this final so that separable calculator
* can throw an exception on it since it does not support it
*
* @return
*/
public double getLastMax() {
return max;
}
/**
* Return the last computed min local value of the measure.
* Not declaring this final so that separable calculator
* can throw an exception on it since it does not support it
*
* @return
*/
public double getLastMin() {
return min;
}
/**
* Return the last computed standard deviation of
* local values of the measure.
*/
public final double getLastStd() {
return std;
}
/**
* Get the number of samples to be used for the PDFs here
* which have been supplied by calls to
* "setObservations", "addObservations" etc.
*
* <p>Note that the number of samples may not be equal to the length of time-series
* supplied (e.g. for transfer entropy, where we need to accumulate
* a number of samples for the past history of the destination).
* </p>
*
* @return the number of samples to be used for the PDFs
*/
public final int getNumObservations() {
return observations;
}
/**
* Return whether a given integer is a power of 2
*
* @param num an integer
* @return whether the integer is a power of 2
*/
public final static boolean isPowerOf2(int num) {
int bits = 0;
int shiftedValue = num;
@ -156,15 +215,17 @@ public abstract class InfoMeasureCalculator {
}
/**
* Compute the average value of the measure from the previously supplied
* observations
* Compute the average value of the measure
* from the previously-supplied samples.
*
* @return average value
* @return the estimate of the measure
*/
public abstract double computeAverageLocalOfObservations();
/**
* @param debug the debug status to set
* Set or clear debug mode for extra debug printing to stdout
*
* @param debug new setting for debug mode (on/off)
*/
public void setDebug(boolean debug) {
this.debug = debug;

View File

@ -22,16 +22,42 @@ import infodynamics.utils.MathsUtils;
import infodynamics.utils.MatrixUtils;
/**
* Usage:
* 1. Continuous accumulation of observations:
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations()
* 2. Standalone:
* Call: localMultiInformation()
* <p>Multi-information or integration calculator for multivariate discrete data.
* That is, it is applied to <code>double[][]</code> data, where the first index
* is observation number or time, and the second is variable number.
* See Tononi et al. below for the definition of multi-information/integration.</p>
* </p>
*
* @author Joseph Lizier
*
* <p>Usage of the class is intended to follow this paradigm:</p>
* <ol>
* <li>Construct the calculator:
* {@link #MultiInformationCalculator(int, int)};</li>
* <li>Initialise the calculator using {@link #initialise()};</li>
* <li>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</li>
* <li>Compute the required quantities, being (at this stage):
* <ul>
* <li>the average MI: {@link #computeAverageLocalOfObservations()};</li>
* </ul>
* </li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p><b>References:</b><br/>
* <ul>
* <li>"G. Tononi, O. Sporns, G. M. Edelman,
* <a href="http://dx.doi.org/10.1073/pnas.91.11.5033">"A measure for
* brain complexity:
* relating functional segregation and integration in the nervous system"</a>
* Proceedings of the National Academy of Sciences, Vol. 91, No. 11.
* (1994), pp. 5033-5037.</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class MultiInformationCalculator extends InfoMeasureCalculator {
@ -43,8 +69,12 @@ public class MultiInformationCalculator extends InfoMeasureCalculator {
private boolean checkedFirst = false;
/**
* Constructor
*
* Construct an instance
*
* @base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @numVars numbers of joint variables that multi-info
* will be computed over.
*/
public MultiInformationCalculator(int base, int numVars) {
super(base);
@ -54,22 +84,24 @@ public class MultiInformationCalculator extends InfoMeasureCalculator {
marginalCounts = new int[numVars][base];
}
/**
* Initialise calculator, preparing to take observation sets in
*
*/
@Override
public void initialise(){
super.initialise();
MatrixUtils.fill(jointCount, 0);
MatrixUtils.fill(marginalCounts, 0);
}
// TODO Define just a simple addObservations for
// int[][] states where there are just numVars variables
/**
* Add observations of the variables based at every point, with the set defined by
* the group offsets array
* Given one time sample of a homogeneous array of variables (states),
* add the observations of all sets of numVars of these, defined
* by the offsets in groupOffsets from every point in the array.
*
* @param states
* @param groupOffsets
* @param states current values of an array of variables
* @param groupOffsets offsets for the numVars set from a given
* data point
*/
public void addObservations(int[] states, int[] groupOffsets) {
for (int c = 0; c < states.length; c++) {
@ -87,11 +119,15 @@ public class MultiInformationCalculator extends InfoMeasureCalculator {
}
/**
* Add observations of the variables based at every point, with the set defined by
* the group offsets array
* Given one time sample of a homogeneous array of variables (states),
* add the observations of one set of numVars of these, defined
* by the offsets in groupOffsets from destinationIndex.
*
* @param states
* @param groupOffsets
* @param states current values of an array of variables
* @param destinationIndex which variable to center
* our offsets from and take the sample,
* @param groupOffsets offsets for the numVars set from a given
* data point
*/
public void addObservations(int[] states, int destinationIndex, int[] groupOffsets) {
// Add the marginal observations in, and compute the joint state value
@ -107,11 +143,15 @@ public class MultiInformationCalculator extends InfoMeasureCalculator {
}
/**
* Add observations of the variables based at every point, with the set defined by
* the group offsets array
* Given multiple time samples of a homogeneous array of variables (states),
* add the observations of all sets of numVars of these, defined
* by the offsets in groupOffsets from every point in the array.
* Do this for every time point
*
* @param states
* @param groupOffsets
* @param states 2D array of values of an array of variables
* at many observations (first index is time, second is variable index)
* @param groupOffsets offsets for the numVars set from a given
* data point
*/
public void addObservations(int[][] states, int[] groupOffsets) {
for (int t = 0; t < states.length; t++) {
@ -130,12 +170,7 @@ public class MultiInformationCalculator extends InfoMeasureCalculator {
}
}
/**
* Returns the average local multi information storage from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public double computeAverageLocalOfObservations() {
int[] jointTuple = new int[numVars];
@ -146,7 +181,7 @@ public class MultiInformationCalculator extends InfoMeasureCalculator {
}
/**
* Compute the contribution to the MI for all tuples starting with tuple[0..(fromIndex-1)].
* Private utility to compute the contribution to the MI for all tuples starting with tuple[0..(fromIndex-1)].
*
* @param tuple
* @param fromIndex

View File

@ -25,18 +25,43 @@ import infodynamics.utils.MatrixUtils;
import infodynamics.utils.EmpiricalMeasurementDistribution;
import infodynamics.utils.RandomGenerator;
/**
* Usage:
* 1. Continuous accumulation of observations:
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations()
* 2. Standalone:
* Call: localMutualInformation()
* <p>Mutual information (MI) calculator for univariate discrete (int[]) data.</p>
*
* @author Joseph Lizier, joseph.lizier at gmail.com
*
* <p>Usage of the class is intended to follow this paradigm:</p>
* <ol>
* <li>Construct the calculator: {@link #MutualInformationCalculator(int)}
* or {@link #MutualInformationCalculator(int, int)};</li>
* <li>Initialise the calculator using {@link #initialise()};</li>
* <li>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</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average MI: {@link #computeAverageLocalOfObservations()};</li>
* <li>local MI values, such as
* {@link #computeLocalFromPreviousObservations(int[], int[])};</li>
* <li>comparison to null distribution, such as
* {@link #computeSignificance()};</li>
* <li>and variants of these.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[][], int, int)}.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p><b>References:</b><br/>
* <ul>
* <li>T. M. Cover and J. A. Thomas, 'Elements of Information
Theory' (John Wiley & Sons, New York, 1991).</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class MutualInformationCalculator extends InfoMeasureCalculator
implements ChannelCalculator, AnalyticNullDistributionComputer {
@ -50,8 +75,10 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
/**
* Construct a new MI calculator with default time difference of 0
* between the variables
*
* @param base number of symbols for each variable
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @throws Exception
*/
public MutualInformationCalculator(int base) throws Exception {
@ -61,7 +88,8 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
/**
* Create a new mutual information calculator
*
* @param base number of symbols for each variable
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @param timeDiff number of time steps across which to compute
* MI for given time series
* @throws Exception when timeDiff < 0
@ -77,10 +105,7 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
jCount = new int[base];
}
/**
* Initialise calculator, preparing to take observation sets in
*
*/
@Override
public void initialise(){
super.initialise();
miComputed = false;
@ -90,10 +115,13 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
}
/**
* Add more observations in to our estimates of the pdfs
* Pairs are between the arrays var1 and var2, separated in time by timeDiff (i is first)
* {@inheritDoc}
*
* Pairs for MI are between the arrays var1 and var2, separated in time by timeDiff
* (var1 is first).
*
*/
@Override
public void addObservations(int[] var1, int[] var2) {
int timeSteps = var1.length;
// int columns = states[0].length;
@ -113,10 +141,12 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
}
/**
* Add more observations in to our estimates of the pdfs
* Pairs are between columns iCol and jCol, separated in time by timeDiff (i is first)
* {@inheritDoc}
*
* Pairs for MI are between columns iCol and jCol, separated in time by timeDiff (i is first).
*
*/
@Override
public void addObservations(int states[][], int iCol, int jCol) {
int rows = states.length;
// int columns = states[0].length;
@ -135,12 +165,7 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
}
}
/**
* Returns the average local mutual information storage from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public double computeAverageLocalOfObservations() {
double mi = 0.0;
double miCont = 0.0;
@ -189,12 +214,7 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
return mi;
}
/**
* Compute the significance of obtaining the given average from the given observations
*
* @param numPermutationsToCheck number of new orderings of the source values to compare against
* @return
*/
@Override
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) {
RandomGenerator rg = new RandomGenerator();
// (Not necessary to check for distinct random perturbations)
@ -203,10 +223,38 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
}
/**
* Compute the significance of obtaining the given average from the given observations
* Generate a bootstrapped distribution of what the MI would look like,
* under a null hypothesis that the source values of our
* samples had no relation to the destination values.
*
* @param newOrderings the reorderings to use
* @return
* <p>See Section II.E "Statistical significance testing" of
* the JIDT paper below for a description of how this is done for
* a mutual information. Basically, the marginal PDFs
* of each marginal
* are preserved, while their joint PDF is destroyed, and the
* distribution of MI under these conditions is generated.</p>
*
* <p>Note that if several disjoint time-series have been added
* as observations using {@link #addObservations(double[])} etc.,
* then these separate "trials" will be mixed up in the generation
* of surrogates here.</p>
*
* <p>This method (in contrast to {@link #computeSignificance(int)})
* allows the user to specify how to construct the surrogates,
* such that repeatable results may be obtained.</p>
*
* @param newOrderings a specification of how to shuffle the next values
* to create the surrogates to generate the distribution with. The first
* index is the permutation number (i.e. newOrderings.length is the number
* of surrogate samples we use to bootstrap to generate the distribution here.)
* Each array newOrderings[i] should be an array of length N (where
* would be the value returned by {@link #getNumObservations()}),
* containing a permutation of the values in 0..(N-1).
* @return the distribution of MI scores under this null hypothesis.
* @see "J.T. Lizier, 'JIDT: An information-theoretic
* toolkit for studying the dynamics of complex systems', 2014."
* @throws Exception where the length of each permutation in newOrderings
* is not equal to the number N samples that were previously supplied.
*/
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) {
double actualMI = computeAverageLocalOfObservations();
@ -264,29 +312,7 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
return measDistribution;
}
/**
* <p>Compute the statistical significance of the mutual information
* result analytically, without creating a distribution
* under the null hypothesis by bootstrapping.</p>
*
* <p>Brillinger (see reference below) shows that under the null hypothesis
* of no source-destination relationship, the MI for two
* discrete distributions follows a chi-square distribution with
* degrees of freedom equal to the product of the number of discrete values
* minus one, for each variable.</p>
*
* @return ChiSquareMeasurementDistribution object
* This object contains the proportion of MI scores from the distribution
* which have higher or equal MIs to ours.
*
* @see Brillinger, "Some data analyses using mutual information",
* {@link http://www.stat.berkeley.edu/~brill/Papers/MIBJPS.pdf}
* @see Cheng et al., "Data Information in Contingency Tables: A
* Fallacy of Hierarchical Loglinear Models",
* {@link http://www.jds-online.com/file_download/112/JDS-369.pdf}
* @see Barnett and Bossomaier, "Transfer Entropy as a Log-likelihood Ratio"
* {@link http://arxiv.org/abs/1205.6339}
*/
@Override
public AnalyticMeasurementDistribution computeSignificance() {
if (!miComputed) {
computeAverageLocalOfObservations();
@ -331,7 +357,6 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
* @return array of local mutual information values for each
* observation of (var1, var2). Note - if timeDiff > 0, then the
* return length will be var1.length - timeDiff.
* @throws Exception
*/
public double[] computeLocalFromPreviousObservations(int[] var1, int[] var2) throws Exception{
@ -371,8 +396,13 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
* for the given states, using pdfs built up from observations previously
* sent in via the addObservations method
*
* @param states
* @return
* @param states 2D time series of observations (first index time,
* second is variable index)
* @param iCol column number for first variable
* @param jCol column number for second variable
* @return array of local mutual information values for each
* observation of (var1, var2). Note - if timeDiff > 0, then the
* return length will be var1.length - timeDiff.
*/
public double[] computeLocalFromPreviousObservations(int states[][], int iCol, int jCol){
int rows = states.length;
@ -415,10 +445,15 @@ public class MutualInformationCalculator extends InfoMeasureCalculator
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
*
* @param states - 2D array of states
* @return
* @param states 2D time series of observations (first index time,
* second is variable index)
* @param iCol column number for first variable
* @param jCol column number for second variable
* @return array of local mutual information values for each
* observation of (var1, var2). Note - if timeDiff > 0, then the
* return length will be var1.length - timeDiff.
*/
public double[] localMutualInformation(int states[][], int iCol, int jCol) {
public double[] computeLocal(int states[][], int iCol, int jCol) {
initialise();
addObservations(states, iCol, jCol);
return computeLocalFromPreviousObservations(states, iCol, jCol);

View File

@ -22,37 +22,64 @@ import infodynamics.utils.MathsUtils;
import infodynamics.utils.MatrixUtils;
/**
* <p>Implements the Predictive information (see Bialek et al. below)
* form of the Excess Entropy (see Crutchfield et al. below),
* i.e. the mutual information I(x_{n+1}^{(k+)};x_{n}^{(k)}) between
* k-length blocks in the past up to time n, (x_{n}^{(k)}), and in the future
* from time n+1, x_{n+1}^{(k+)}</p>
* <p> Predictive information calculator for univariate discrete (int[]) data.
* See definition of Predictive information (PI) by Bialek et al. below,
* also is a form of the Excess Entropy (see Crutchfield and Feldman below),
* Basically, PI is the mutual information between the past <i>state</i>
* of a time-series process <i>X</i> and its future <i>state</i>.
* The past <i>state</i> at time <code>n</code>
* is represented by an embedding vector of <code>k</code> values from <code>X_n</code> backwards,
* each separated by <code>\tau</code> steps, giving
* <code><b>X^k_n</b> = [ X_{n-(k-1)\tau}, ... , X_{n-\tau}, X_n]</code>.
* We call <code>k</code> the embedding dimension, and <code>\tau</code>
* the embedding delay (only delay = 1 is implemented at the moment).
* The future <i>state</i> at time <code>n</code>
* is defined similarly into the future:
* each separated by <code>\tau</code> steps, giving
* <code><b>X^k+_n</b> = [ X_{n+1}, X_{n+\tau}, X_{n+(k-1)\tau}]</code>.
* PI is then the mutual information between <b>X^k_n</b> and <b>X^k+_n</b>.</p>
*
* <p>Usage:
* <p>Usage of the class is intended to follow this paradigm:</p>
* <ol>
* <li>Continuous accumulation of observations - call:
* <ol>
* <li>initialise()</li>
* <li>addObservations() several times over</li>
* <li>computeLocalFromPreviousObservations()</li>
* </ol></li>
* <li>Standalone - call:
* <ol>
* <li>localActiveInformation()</li>
* </ol></li>
* </ol>
* <li>Construct the calculator: {@link #PredictiveInformationCalculator(int, int)};</li>
* <li>Initialise the calculator using {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* sets of {@link #addObservations(int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average entropy: {@link #computeAverageLocalOfObservations()};</li>
* <li>local entropy values, such as {@link #computeLocal(int[])};</li>
* <li>and variants of these.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[])},
* {@link #computeAverageLocal(int[])} etc.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* @author Joseph Lizier joseph.lizier at gmail.com
* <p>TODO Inherit from {@link SingleAgentMeasureInContextOfPastCalculator}
* as {@link ActiveInformationCalculator} does; Tidy up the Javadocs for
* the methods, which are somewhat preliminary</p>
*
* @see <a href="http://dx.doi.org/10.1016/S0378-4371(01)00444-7">
* Bialek, W., Nemenman, I., and Tishby, N. (2001)
* Complexity through nonextensivity. Physica A, 302, 89-99.</a>
* @see <a href="http://dx.doi.org/10.1063/1.1530990">
* Crutchfield, J. P. and Feldman, D. P. (2003) Regularities
* unseen, randomness observed: Levels of entropy convergence.
* Chaos, 13, 25-54.</a>
*
*
* <p><b>References:</b><br/>
* <ul>
* <li>Bialek, W., Nemenman, I., and Tishby, N.,
* <a href="http://dx.doi.org/10.1016/S0378-4371(01)00444-7">
* "Complexity through nonextensivity"</a>,
* Physica A, 302, 89-99. (2001).</li>
* <li>J. P. Crutchfield, D. P. Feldman,
* <a href="http://dx.doi.org/10.1063/1.1530990">
* "Regularities Unseen, Randomness Observed: Levels of Entropy Convergence"</a>,
* Chaos, Vol. 13, No. 1. (2003), pp. 25-54.</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class PredictiveInformationCalculator {
@ -76,13 +103,21 @@ public class PredictiveInformationCalculator {
*
* @param numDiscreteValues Number of discrete values (e.g. 2 for binary states)
* @param blockLength
*
* @deprecated
* @return
*/
public static PredictiveInformationCalculator newInstance(int numDiscreteValues, int blockLength) {
return new PredictiveInformationCalculator(numDiscreteValues, blockLength);
}
/**
* Construct a new instance
*
* @param numDiscreteValues number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
* @param blockLength embedded history length of the past and future to use -
* this is k in Schreiber's notation.
*/
public PredictiveInformationCalculator(int numDiscreteValues, int blockLength) {
super();

View File

@ -25,23 +25,53 @@ import infodynamics.utils.MatrixUtils;
import java.util.Properties;
/**
* Implements separable information (see Lizier et al, Chaos 2010)
* Separable information = sum of active information and apparent transfer entropy from every
* Implements <b>separable information</b> (see Lizier et al, 2010, below).
*
* Separable information is the sum of active information and apparent transfer entropy from every
* causal information contributor.
* The causal information contributors (either their offsets or their absolute column numbers)
* should be supplied in the same order in every method call, otherwise the answer supplied will
* be incorrect.
*
* Usage:
* 1. Continuous accumulation of observations:
* Call: a. initialise()
* b. addObservations() several times over
* c. computeLocalFromPreviousObservations()
* 2. Standalone:
* Call: localActiveInformation()
*
* @author Joseph Lizier
*
* <p>
* Usage of the child classes implementing this interface is intended to follow this paradigm:
* </p>
* <ol>
* <li>Construct the calculator via
* {@link #SeparableInfoCalculator(int, int, int)};</li>
* <li>Initialise the calculator using
* {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* the set of {@link #addObservations(int[][], int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average TE: {@link #computeAverageLocalOfObservations()};</li>
* <li>the local TE values for these samples: {@link #computeLocalOfPreviousObservations()}</li>
* <li>local TE values for a specific set of samples: e.g.
* {@link #computeLocalFromPreviousObservations(int[][], int[])} etc.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[][], int[])} or
* {@link #computeAverageLocal(int[][], int[])}.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* <p>The causal information contributors (either their offsets or their absolute column numbers)
* should be supplied in the same order in every method call, otherwise the answer supplied will
* be incorrect.</p>
*
* <p><b>References:</b><br/>
* <ul>
* <li>J. T. Lizier, M. Prokopenko and A. Zomaya,
* <a href=http://dx.doi.org/10.1063/1.3486801">
* "Information modification and particle collisions in distributed computation"</a>
* Chaos 20, 3, 037109 (2010).</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
@ -94,7 +124,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
//TODO make this class compatible with k==0
// (low priority, not truly necessary)
throw new RuntimeException("This class does not currently " +
"function with k < 1 (see CompleteTransferEntropyCalculator " +
"function with k < 1 (see ConditionalTransferEntropyCalculator " +
"for how to implement this)");
}
@ -109,11 +139,35 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
}
}
/**
* Construct an instance
*
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @param history embedded history length of the destination to condition on -
* this is k in Schreiber's notation.
* @param numOtherInfoContributors number of information contributors
* (other than the past of the destination
* or the source) to condition on.
*/
public SeparableInfoCalculator
(int base, int history, int numInfoContributors) {
this(base, history, numInfoContributors, false);
}
/**
* Private method to implement the public constructor
*
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @param history embedded history length of the destination to condition on -
* this is k in Schreiber's notation.
* @param numOtherInfoContributors number of information contributors
* (other than the past of the destination
* or the source) to condition on.
* @param dontCreateObsStorage indicates that storage for
* observations should not be created.
*/
protected SeparableInfoCalculator
(int base, int history, int numInfoContributors, boolean dontCreateObsStorage) {
@ -131,12 +185,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
}
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
*
*/
@Override
public void initialise(){
super.initialise();
@ -159,7 +208,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* agents will contribute to single pdfs, and all are assumed
* to have other info contributors at same offsets.
*
* @param states states 1st index is time, 2nd index is agent number
* @param states multivariate time series, 1st index is time, 2nd index is agent number
* @param offsetOfDestFromSources offsets of the destination *from* causal information contributors.
* (i.e. an offset of 1 means the destination is one index larger, or one to the right,
* than the source).
@ -238,7 +287,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* agents will contribute to single pdfs, and all are assumed
* to have other info contributors at same offsets.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param states multivariate time series 1st index is time, 2nd and 3rd index give the 2D agent number
* @param offsetOfDestFromSources 2D offsets of the destination *from* causal information contributors.
* (i.e. an offset of 1 means the destination is one index larger, or one to the right,
* than the source).
@ -334,12 +383,12 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
}
/**
* Add observations for a single source-destination pair of the multi-agent system
* Add observations for a single destination of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states the space-time observations to compute over
* @param states multivariate time series, 1st index is time, 2nd index is agent number
* @param destCol the destination index
* @param sourcesAbsolute array of the source indices
*/
@ -394,12 +443,13 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
}
/**
* Add observations for a single source-destination pair of the multi-agent system
* Add observations for a single destination pair of the
* multivariate time series
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param states multivariate time series: 1st index is time, 2nd and 3rd index give the 2D agent number
* @param destAgentRow the destination index
* @param destAgentColumn the destination index
* @param sourcesAbsolute array of the source indices
@ -455,12 +505,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
}
}
/**
* Returns the average local separable information from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public synchronized double computeAverageLocalOfObservations() {
max = 0;
min = 0;
@ -469,7 +514,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
avPositiveLocal = 0.0;
avNegativeLocal = 0.0;
if (computeMultiInfoCoherence) {
miCalc.startIndividualObservations();
miCalc.startAddObservations();
}
// Create space for the joint source values to run through:
@ -479,7 +524,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Close off the individual observations
try {
if (computeMultiInfoCoherence) {
miCalc.endIndividualObservations();
miCalc.finaliseAddObservations();
}
} catch (Exception e) {
// an exception would only be thrown if we changed the number of causal contributors here
@ -489,8 +534,11 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
std = Math.sqrt(meanSqLocals - average * average);
return average;
}
/**
* Updates the average, max, min and meanSq of locals for the separable information
* Private utility function.
*
* <p>Updates the average, max, min and meanSq of locals for the separable information
* for the given source values in sourceValues up to the index indexToModify over
* all possible source values after the index indexToModify onwards. Uses recursion
* on increasing indexToModify.
@ -588,13 +636,16 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* states, using pdfs built up from observations previously
* sent in via the addObservations method.
* This method to be used for homogeneous agents only
* since it assumes the source offsets are the same
* for all destinations (and all are added to the PDFs)
*
* @param states 1st index is time, 2nd is agent index
* @param states multivariate time series 1st index is time, 2nd is agent index
* @param offsetOfDestFromSources offsets of the destination *from* causal information contributors.
* (i.e. an offset of 1 means the destination is one index larger, or one to the right,
* than the source).
* sourcesOffsets is permitted to include 0, it will be ignored.
* @return
* @return multivariate time series of local separable information,
* indexed as per states.
*/
public double[][] computeLocalFromPreviousObservations
(int states[][], int offsetOfDestFromSources[]){
@ -640,7 +691,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Make a vector of the active and TE values for the coherence computation
if (computeMultiInfoCoherence) {
miCalc.startIndividualObservations();
miCalc.startAddObservations();
}
double[] localActAndTes = new double[numSources + 1];
@ -708,7 +759,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Close off the individual observations
try {
if (computeMultiInfoCoherence) {
miCalc.endIndividualObservations();
miCalc.finaliseAddObservations();
}
} catch (Exception e) {
// an exception would only be thrown if we changed the number of causal contributors here
@ -724,13 +775,16 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* states, using pdfs built up from observations previously
* sent in via the addObservations method.
* This method to be used for homogeneous agents only
* since it assumes offsets are same for all destinations
* and includes them all in the calculation
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param states multivariate time series 1st index is time, 2nd and 3rd index give the 2D agent number
* @param offsetOfDestFromSources offsets of the destination *from* causal information contributors.
* (i.e. an offset of 1 means the destination is one index larger, or one to the right,
* than the source).
* sourcesOffsets is permitted to include 0, it will be ignored.
* @return
* @return multivariate time series of local separable information
* values, indexed as per states.
*/
public double[][][] computeLocalFromPreviousObservations
(int states[][][], int offsetOfDestFromSources[][]){
@ -784,7 +838,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Make a vector of the active and TE values for the coherence computation
if (computeMultiInfoCoherence) {
miCalc.startIndividualObservations();
miCalc.startAddObservations();
}
double[] localActAndTes = new double[numSources + 1];
@ -865,7 +919,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Close off the individual observations
try {
if (computeMultiInfoCoherence) {
miCalc.endIndividualObservations();
miCalc.finaliseAddObservations();
}
} catch (Exception e) {
// an exception would only be thrown if we changed the number of causal contributors here
@ -878,14 +932,16 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Computes local separable information for the given
* states, using pdfs built up from observations previously
* destination in the multivariate time series,
* using pdfs built up from observations previously
* sent in via the addObservations method.
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd index is agent number
* @param states multivariate time series 1st index is time, 2nd index is agent number
* @param destCol index for the destination agent
* @param sourcesAbsolute indices for the source agents
* @return
* @return multivariate time series of local separable information
* values for the given destination variable
*/
public double[] computeLocalFromPreviousObservations
(int states[][], int destCol, int[] sourcesAbsolute){
@ -925,7 +981,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Make a vector of the active and TE values for the coherence computation
if (computeMultiInfoCoherence) {
miCalc.startIndividualObservations();
miCalc.startAddObservations();
}
double[] localActAndTes = new double[numSources + 1];
@ -985,7 +1041,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Close off the individual observations
try {
if (computeMultiInfoCoherence) {
miCalc.endIndividualObservations();
miCalc.finaliseAddObservations();
}
} catch (Exception e) {
// an exception would only be thrown if we changed the number of causal contributors here
@ -998,15 +1054,18 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Computes local separable information for the given
* states, using pdfs built up from observations previously
* destination variable in a multivariate time series,
* using pdfs built up from observations previously
* sent in via the addObservations method.
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param states multivariate time series: 1st index is time,
* 2nd and 3rd index give the 2D agent number
* @param destAgentRow the destination index
* @param destAgentColumn the destination index
* @param sourcesAbsolute array of the source indices
* @return
* @return multivariate time series of local separable information
* values for the given destination
*/
public double[] computeLocalFromPreviousObservations
(int states[][][], int destAgentRow, int destAgentColumn, int[][] sourcesAbsolute){
@ -1046,7 +1105,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Make a vector of the active and TE values for the coherence computation
if (computeMultiInfoCoherence) {
miCalc.startIndividualObservations();
miCalc.startAddObservations();
}
double[] localActAndTes = new double[numSources + 1];
@ -1107,7 +1166,7 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
// Close off the individual observations
try {
if (computeMultiInfoCoherence) {
miCalc.endIndividualObservations();
miCalc.finaliseAddObservations();
}
} catch (Exception e) {
// an exception would only be thrown if we changed the number of causal contributors here
@ -1120,18 +1179,22 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Standalone routine to
* compute local separable info across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* compute local separable info across a 2D multivariate time series
* of the states of homogeneous agents
* Return a 2D spatiotemporal multivariate time series of local values.
* First history rows are zeros.
* This method to be called for homogeneous agents only
* since it assumes all destinations have the same source offsets
* and includes them all in the calculation.
*
* @param states - 2D array of states
* @param states multivariate time series: first index is time,
* second is variable index.
* @param offsetOfDestFromSources offsets of the destination *from* causal information contributors.
* (i.e. an offset of 1 means the destination is one index larger, or one to the right,
* than the source).
* sourcesOffsets is permitted to include 0, it will be ignored.
* @return
* @return multivariate time series of local separable information,
* indexed as per states.
*/
public double[][] computeLocal(int states[][], int[] offsetOfDestFromSources) {
@ -1143,18 +1206,21 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Standalone routine to
* compute local separable info across a 3D spatiotemporal
* array of the states of homogeneous agents
* compute local separable info across a 3D multivariate time series
* of the states of homogeneous agents.
* Return a 3D spatiotemporal array of local values.
* First history rows are zeros
* First history rows are zeros.
* This method to be called for homogeneous agents only
* since it assumes all destinations have the same source offsets
* and includes them all in the calculation.
*
* @param states 1st index is time, 2nd and 3rd are agent indices
* @param states multivariate time series: 1st index is time, 2nd and 3rd are agent indices
* @param offsetOfDestFromSources offsets of the destination *from* causal information contributors.
* (i.e. an offset of 1 means the destination is one index larger, or one to the right,
* than the source).
* sourcesOffsets is permitted to include 0, it will be ignored.
* @return
* @return multivariate time series of local separable information,
* indexed as per states.
*/
public double[][][] computeLocal(int states[][][], int[][] offsetOfDestFromSources) {
@ -1166,14 +1232,19 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Standalone routine to
* compute average local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* compute average separable information across a 2D multivariate time series
* of the states of homogeneous agents.
* Return the average.
* This method to be called for homogeneous agents only
* since it assumes all destinations have the same source offsets
* and includes them all in the calculation.
*
* @param states - 2D array of states
* @param sourceOffsets - column offsets for causal info contributors
* @return
* @param states multivariate time series: first index is time,
* second is variable number
* @param sourceOffsets column offsets for causal info contributors
* from each destination
* @return average separable information for these
* observations.
*/
public double computeAverageLocal(int states[][], int[] sourceOffsets) {
@ -1184,16 +1255,18 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Standalone routine to
* compute average local transfer entropy across a 3D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* compute average separable information across a 3D multivariate time series
* of the states of homogeneous agents.
* Return the average separable info.
* This method to be called for homogeneous agents only
* since it assumes all destinations have the same source offsets
* and includes them all in the calculation.
*
* @param states 1st index is time, 2nd and 3rd are agent indices
* @param states multivariate time series: 1st index is time, 2nd and 3rd are agent indices
* @param sourceOffsets agent offsets for causal info contributors. 1st index points to
* an array of two elements for the row and column offsets.
*
* @return
* @return average separable information for these
* observations.
*/
public double computeAverageLocal(int states[][][], int[][] sourceOffsets) {
@ -1204,16 +1277,19 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Standalone routine to
* compute local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* compute local separable information for a specific destination
* in a 2D multivariate time series
* of the states.
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* First history rows are zeros.
* This method suitable for heterogeneous agents
*
* @param states - 2D array of states
* @param destCol - column index for the destination agent
* @param sourcesAbsolute - column indices for causal info contributors
* @return
* @param states multivariate time series: first index is time,
* second is variable number
* @param destCol column index for the destination agent to consider
* @param sourcesAbsolute column indices for causal info contributors
* to this destination
* @return time series of local separable information
*/
public double[] computeLocal(int states[][], int destCol, int[] sourcesAbsolute) {
@ -1225,17 +1301,17 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Standalone routine to
* compute local transfer entropy across a 3D spatiotemporal
* array of the states of homogeneous agents
* Return a 3D spatiotemporal array of local values.
* First history rows are zeros
* This method suitable for heterogeneous agents
* compute local separable information for a specific destination
* in a 3D multivariate time series
* of the states.
* First history rows are zeros.
* This method suitable for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd are agent indices
* @param states multivariate time series: 1st index is time, 2nd and 3rd are agent indices
* @param destAgentRow row index for the destination agent
* @param destAgnetColumn column index for the destination agent
* @param sourcesAbsolute absolute indices for causal info contributors to this destination
* @return
* @return time series of local separable information
*/
public double[] computeLocal(int states[][][], int destAgentRow,
int destAgentColumn, int[][] sourcesAbsolute) {
@ -1248,15 +1324,17 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Standalone routine to
* compute average local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* Returns the average
* compute average separable information for a specific destination
* in a 2D multivariate time series
* of the states.
* Returns the average.
* This method suitable for heterogeneous agents
*
* @param states - 2D array of states
* @param destCol - column index for the destination agent
* @param sourcesAbsolute - column indices for causal info contributors
* @return
* @param states multivariate time series: first index is time,
* second is variable number
* @param destCol column index for the destination agent
* @param sourcesAbsolute column indices for causal info contributors
* @return average separable information for this destination
*/
public double computeAverageLocal(int states[][], int destCol, int[] sourcesAbsolute) {
@ -1267,16 +1345,17 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
/**
* Standalone routine to
* compute average local transfer entropy across a 3D spatiotemporal
* array of the states of homogeneous agents
* Returns the average
* compute average separable information for a specific destination
* in a 3D multivariate time series
* of the states.
* Returns the average.
* This method suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd indices are agent indices
* @param states multivariate time series: 1st index is time, 2nd and 3rd are agent indices
* @param destAgentRow row index for the destination agent
* @param destAgnetColumn column index for the destination agent
* @param sourcesAbsolute absolute indices for causal info contributors to this destination
* @return
* @return average separable information for this destination
*/
public double computeAverageLocal(int states[][][], int destAgentRow,
int destAgentColumn, int[][] sourcesAbsolute) {
@ -1318,8 +1397,8 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* Counts the information contributors to this node which
* are not equal to the node itself (offset 0)
*
* @param sourcesOffsets
* @return
* @param sourcesOffsets information contributors
* @return count of sourcesOffsets with any 0 entries removed from the array
*/
public static int countOfOffsetSources(int[] sourcesOffsets) {
int countOfSources = 0;
@ -1335,8 +1414,8 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* Counts the information contributors to this node which
* are not equal to the node itself (offset (0,0))
*
* @param sourcesOffsets
* @return
* @param sourcesOffsets information contributors
* @return count of sourcesOffsets with any (0,0) entries removed from the 2D array
*/
public static int countOfOffsetSources(int[][] sourcesOffsets) {
int countOfSources = 0;
@ -1352,9 +1431,9 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* Counts the information contributors to the dest which
* are not equal to the node itself
*
* @param sources
* @param dest
* @return
* @param sources array of source indices
* @param dest index of dest
* @return count of sources with any entries equal to dest removed.
*/
public static int countOfAbsoluteSources(int[] sources, int dest) {
int countOfSources = 0;
@ -1371,8 +1450,9 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* are not equal to the node itself
*
* @param sources array of arrays of row and column indices
* @param dest
* @return
* @param destAgentRow row of dest variable
* @param destAgentColumn column of dest variable
* @return count of entries in sources not equal to the dest indices
*/
public static int countOfAbsoluteSources(int[][] sources, int destAgentRow, int destAgentColumn) {
int countOfSources = 0;
@ -1389,8 +1469,9 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* Check that the supplied array of offsets as sources
* is long enough compared to our expectation
*
* @param sourcesOffsets
* @return
* @param sourcesOffsets array of source offsets
* @return whether it is as long as expected
* @throws RuntimeException if it is not
*/
public boolean confirmEnoughOffsetSources(int[] sourcesOffsets) {
if (countOfOffsetSources(sourcesOffsets) != numSources) {
@ -1403,8 +1484,9 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* Check that the supplied array of offsets as sources
* is long enough compared to our expectation
*
* @param sourcesOffsets
* @return
* @param sourcesOffsets array of 2D source offsets
* @return whether it is as long as expected
* @throws RuntimeException if it is not
*/
public boolean confirmEnoughOffsetSources(int[][] sourcesOffsets) {
if (countOfOffsetSources(sourcesOffsets) != numSources) {
@ -1417,9 +1499,10 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* Check that the supplied array of absolutes as sources
* is long enough compared to our expectation
*
* @param sourcesAbsolute
* @param dest
* @return
* @param sourcesAbsolute array of source indices
* @param dest dest index
* @return whether it is as long as expected
* @throws RuntimeException if it is not
*/
public boolean confirmEnoughAbsoluteSources(int[] sourcesAbsolute, int dest) {
if (countOfAbsoluteSources(sourcesAbsolute, dest) != numSources) {
@ -1432,10 +1515,11 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* Check that the supplied array of absolutes as sources
* is long enough compared to our expectation
*
* @param sourcesAbsolute
* @param destAgentRow
* @param destAgentColumn
* @return
* @param sourcesAbsolute array of 2D source indices
* @param destAgentRow row of dest variable
* @param destAgentColumn column of dest variable
* @return whether it is as long as expected
* @throws RuntimeException if it is not
*/
public boolean confirmEnoughAbsoluteSources(int[][] sourcesAbsolute, int destAgentRow, int destAgentColumn) {
if (countOfAbsoluteSources(sourcesAbsolute, destAgentRow, destAgentColumn) != numSources) {
@ -1449,8 +1533,9 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* are not equal to the node itself (offset 0).
* Checks that there are enough sources.
*
* @param sourcesOffsets
* @return
* @param sourcesOffsets array of source offsets
* @return sourcesOffsets with these entries removed
* from the array
*/
public int[] cleanOffsetOfDestFromSources(int[] sourcesOffsets) {
int[] cleaned = new int[numSources];
@ -1481,7 +1566,8 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
*
* @param sourcesOffsets 2D source offsets. 1st dimension is source index, 2nd index is for
* 1st or 2nd index of source indice pair
* @return
* @return sourcesOffsets with these entries removed
* from the array
*/
public int[][] cleanOffsetOfDestFromSources(int[][] sourcesOffsets) {
int[][] cleaned = new int[numSources][2];
@ -1512,9 +1598,10 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* are not equal to the node itself (offset 0).
* Checks that there are enough other information contributors.
*
* @param sources
* @param dest
* @return
* @param sources array of source indices
* @param dest dest index
* @return sources with these entries removed
* from the array
*/
public int[] cleanAbsoluteSources(int[] sources, int dest) {
int[] cleaned = new int[numSources];
@ -1544,9 +1631,10 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
* are not equal to the node itself (offset 0).
* Checks that there are enough other information contributors.
*
* @param sources
* @param dest
* @return
* @param sources 2D array of source indices
* @param dest dest index
* @return sources with these entries removed
* from the array
*/
public int[][] cleanAbsoluteSources(int[][] sources, int destAgentRow, int destAgentColumn) {
int[][] cleaned = new int[numSources][2];
@ -1573,13 +1661,31 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
return cleaned;
}
/**
* Whether we use periodic boundary conditions when considering
* spatiotemporal data for homogeneous variables
*
* @return if this is the case
*/
public boolean isPeriodicBoundaryConditions() {
return periodicBoundaryConditions;
}
/**
* Set whether to use periodic boundary conditions
*
* @param periodicBoundaryConditions as above
*/
public void setPeriodicBoundaryConditions(boolean periodicBoundaryConditions) {
this.periodicBoundaryConditions = periodicBoundaryConditions;
}
/**
* Whether the calculator should gather observations to
* compute a multi-info between the local TEs and
* AIS values.
* See Lizier et al, 2012 "Coherent information structure in complex computation"
*
*/
public boolean isComputeMultiInfoCoherence() {
return computeMultiInfoCoherence;
}
@ -1627,11 +1733,14 @@ public class SeparableInfoCalculator extends ContextOfPastMeasureCalculator {
return true;
}
// Allows reclaiming of some vital memory
/**
* Allows reclaiming of some vital memory
*/
public void resetMultiInfoCoherenceCalculator() {
miCalc.initialise(numSources + 1);
}
@Override
public void setDebug(boolean debug) {
if (computeMultiInfoCoherence) {
miCalc.setDebug(debug);

View File

@ -20,6 +20,26 @@ package infodynamics.measures.discrete;
import infodynamics.utils.MatrixUtils;
/**
* Implements <b>separable information</b> (see Lizier et al, 2010, below),
* by using separate Transfer Entropy and Active information storage
* calculators.
*
* Javadocs are preliminary here (TODO), please see
* {@link SeparableInfoCalculator} for user-level documentation
* regarding the methods.
*
* <p><b>References:</b><br/>
* <ul>
* <li>J. T. Lizier, M. Prokopenko and A. Zomaya,
* <a href=http://dx.doi.org/10.1063/1.3486801">
* "Information modification and particle collisions in distributed computation"</a>
* Chaos 20, 3, 037109 (2010).</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public class SeparableInfoCalculatorByAddition extends SeparableInfoCalculator {
ActiveInformationCalculator aiCalc;
@ -533,7 +553,7 @@ public class SeparableInfoCalculatorByAddition extends SeparableInfoCalculator {
// Now compute the coherence of computation
if (computeMultiInfoCoherence) {
miCalc.startIndividualObservations();
miCalc.startAddObservations();
double[] miTuple = new double[numSources + 1];
for (int t = k; t < timeSteps; t++) {
// Construct the multi-info tuple
@ -544,7 +564,7 @@ public class SeparableInfoCalculatorByAddition extends SeparableInfoCalculator {
miCalc.addObservation(miTuple);
}
try {
miCalc.endIndividualObservations();
miCalc.finaliseAddObservations();
} catch (Exception e) {
// an exception would only be thrown if we changed the number of causal contributors here
// which simply will not happen. Just in case it does, we'll throw a runtime exception
@ -612,7 +632,7 @@ public class SeparableInfoCalculatorByAddition extends SeparableInfoCalculator {
// Now compute the coherence of computation
if (computeMultiInfoCoherence) {
miCalc.startIndividualObservations();
miCalc.startAddObservations();
double[] miTuple = new double[numSources + 1];
for (int t = k; t < timeSteps; t++) {
for (int r = periodicBoundaryConditions ? 0 : nonPeriodicStartAgent;
@ -627,7 +647,7 @@ public class SeparableInfoCalculatorByAddition extends SeparableInfoCalculator {
}
}
try {
miCalc.endIndividualObservations();
miCalc.finaliseAddObservations();
} catch (Exception e) {
// an exception would only be thrown if we changed the number of causal contributors here
// which simply will not happen. Just in case it does, we'll throw a runtime exception
@ -720,7 +740,7 @@ public class SeparableInfoCalculatorByAddition extends SeparableInfoCalculator {
// Now compute the coherence of computation
if (computeMultiInfoCoherence) {
miCalc.startIndividualObservations();
miCalc.startAddObservations();
double[] miTuple = new double[numSources + 1];
for (int t = k; t < timeSteps; t++) {
for (int r = periodicBoundaryConditions ? 0 : nonPeriodicStartRow;
@ -739,7 +759,7 @@ public class SeparableInfoCalculatorByAddition extends SeparableInfoCalculator {
}
}
try {
miCalc.endIndividualObservations();
miCalc.finaliseAddObservations();
} catch (Exception e) {
// an exception would only be thrown if we changed the number of causal contributors here
// which simply will not happen. Just in case it does, we'll throw a runtime exception

View File

@ -19,35 +19,54 @@
package infodynamics.measures.discrete;
/**
* Interface to define adding observations and calculating
* local and average values of info theoretic measures
* for single agent metrics (e.g. entropy, active information).
* Would ideally be an abstract class to be inherited from, but
* it's more important for us to have inheritance from
* Interface for calculators of information-theoretic measures
* for single variables (e.g. entropy, active information storage).
* The interface defines common operations such as
* adding observations and calculating
* local and average values, etc.
*
* <p>Usage is as per {@link InfoMeasureCalculator}, with
* many methods for supplying observations and making
* calculations defined here.</p>
*
* <p>It would ideally be an abstract class to be inherited from, but
* it's more important for some of our calculators to have inheritance from
* ContextOfPastCalculator, and since java doesn't allow multiple
* inheritance, one of them has to miss out.
* To get around this, we combine the two in
* {@link SingleAgentMeasureInContextOfPastCalculator}.
* </p>
*
* @author Joseph Lizier
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public interface SingleAgentMeasure {
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd index is agent number
* @param states series of samples
*/
public void addObservations(int states[]);
/**
* Add observations in to our estimates of the pdfs.
* This call suitable only for homogeneous agents, as all
* agents will contribute to the PDFs.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
*/
public void addObservations(int states[][]);
/**
* Add observations for a single agent of the multi-agent system
* Add observations for a single variable of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* This call should be made as opposed to {@link #addObservations(int[][])}
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd index is agent number
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param col index of agent
*/
public void addObservations(int states[][], int col);
@ -57,55 +76,77 @@ public interface SingleAgentMeasure {
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
*/
public void addObservations(int states[][][]);
/**
* Add observations for a single agent of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* This call should be made as opposed to {@link #addObservations(int[][][])}
* for computing active info for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @param index1 first index of agent
* @param index2 index of agent in 2nd dimension
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param index1 row index index the variable
* @param index2 column index of the variable
*/
public void addObservations(int states[][][], int index1, int index2);
/**
* Returns the average information theoretic measure from
* the observed values which have been passed in previously.
*
* Compute the average value of the measure
* from the previously-supplied samples.
*
* Must set average, min and max
*
* @return
* @return the estimate of the measure
*/
public double computeAverageLocalOfObservations();
/**
* Computes local information theoretic measure for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
* sent in via the addObservations method.
*
* Must set average, min and max
*
* @param states 1st index is time, 2nd index is agent number
* @return
* @param states time series of samples
* @return time-series of local values (indexed as per states)
*/
public double[] computeLocalFromPreviousObservations(int states[]);
/**
* Computes local information theoretic measure for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method.
* This method to be used for homogeneous agents only,
* since the local values will be computed for all variables.
*
* Must set average, min and max
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @return 2D time-series of local values (indexed as per states)
*/
public double[][] computeLocalFromPreviousObservations(int states[][]);
/**
* Computes local information theoretic measure for the given
* variable in the 2D time-series
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
* sent in via the addObservations method.
* This method is suitable for heterogeneous agents, since
* the specific variable is identified.
*
* Must set average, min and max
* Must set average, min and max.
*
* @param states 1st index is time, 2nd index is agent number
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param col index of the given variable
* @return time-series of local values for the variable
*/
public double[] computeLocalFromPreviousObservations(int states[][], int col);
@ -113,91 +154,184 @@ public interface SingleAgentMeasure {
* Computes local information theoretic measure for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method to be used for homogeneous agents only
* This method to be used for homogeneous agents only,
* since the local values will be computed for all variables.
*
* Must set average, min and max
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @return 3D time-series of local values (indexed as per states)
*/
public double[][][] computeLocalFromPreviousObservations(int states[][][]);
/**
* Computes local information theoretic measure for the given
* Computes the local information theoretic measure for the given
* variable in the 3D time-series
* states, using pdfs built up from observations previously
* sent in via the addObservations method
* This method is suitable for heterogeneous agents
* This method is suitable for heterogeneous agents, since
* the specific variable is identified.
*
* Must set average, min and max
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param index1 row index of the given variable
* @param index2 column index of the given variable
* @return time-series of local values for the variable
*/
public double[] computeLocalFromPreviousObservations(int states[][][], int index1, int index2);
/**
* Standalone routine to
* compute local information theoretic measure across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* compute the local information-theoretic measure across a
* time-series of states.
* Return a time-series array of local values.
* First history rows are zeros when the measure must build up
* embedded history of the variable.
*
* @param states 1st index is time, 2nd index is agent number
* @return
* @param states time series of samples
* @return time-series of local values (indexed as per states)
*/
public double[] computeLocal(int states[]);
/**
* Standalone routine to
* compute the local information-theoretic measure across a 2D spatiotemporal
* array of the states of homogeneous agents,
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros when the measure must build up
* embedded history of the variable.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @return 2D time-series of local values (indexed as per states)
*/
public double[][] computeLocal(int states[][]);
/**
* Standalone routine to
* compute local information theoretic measure across a 2D spatiotemporal
* compute the local information theoretic measure across a 3D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* Return a 3D spatiotemporal array of local values.
* First history rows are zeros when the measure must build up
* embedded history of the variable.
*
* @param states 1st index is time, 2nd and 3rd index are agent number
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @return 3D time-series of local values (indexed as per states)
*/
public double[][][] computeLocal(int states[][][]);
/**
* Standalone routine to
* compute average local information theoretic measure across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* This method to be called for homogeneous agents only
* compute the average information theoretic measure across a time-series
* of states.
* Return the average.
*
* @param states -1st index is time, 2nd index is agent number
* @return
* @param states time series of samples
* @return average of the information-theoretic measure.
*/
public double computeAverageLocal(int states[]);
/**
* Standalone routine to
* compute the average information theoretic measure across a 2D spatiotemporal
* array of the states of homogeneous agents.
* Return the average.
* This method to be called for homogeneous agents only,
* since all variables are used in the PDFs.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @return average of the information-theoretic measure.
*/
public double computeAverageLocal(int states[][]);
/**
* Standalone routine to
* compute local information theoretic measure for one agent in a 2D spatiotemporal
* array of the states of agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method should be used for heterogeneous agents
* compute the average information theoretic measure across a 3D spatiotemporal
* array of the states of homogeneous agents.
* Return the average.
* This method to be called for homogeneous agents only,
* since all variables are used in the PDFs.
*
* @param states 1st index is time, 2nd index is agent number
* @param col - column number of the agent in the states array
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @return average of the information-theoretic measure.
*/
public double[] computeLocalAtAgent(int states[][], int col);
public double computeAverageLocal(int states[][][]);
/**
* Standalone routine to
* compute average local information theoretic measure
* for a single agent
* Returns the average
* This method suitable for heterogeneous agents
* @param blocksize - Size of blocks to compute entropy over
* @param base - base of the states
* @param states 1st index is time, 2nd index is agent number
* @param col - column number of the agent in the states array
* compute local information theoretic measure for one variable
* in a 2D spatiotemporal
* multivariate array.
* Return a time-series array of local values.
* First history rows are zeros when the measure must build up
* embedded history of the variable.
* This method should be used for heterogeneous agents
*
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param col index of the given variable
* @return time-series of local values of the measure for
* the given variable
*/
public double computeAverageLocalAtAgent(int states[][], int col);
public double[] computeLocal(int states[][], int col);
/**
* Standalone routine to
* compute local information theoretic measure for one variable
* in a 3D spatiotemporal
* multivariate array.
* Return a time-series array of local values.
* First history rows are zeros when the measure must build up
* embedded history of the variable.
* This method should be used for heterogeneous agents
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param index1 row index of the given variable
* @param index2 column index of the given variable
* @return time-series of local values of the measure for
* the given variable
*/
public double[] computeLocal(int states[][][], int index1, int index2);
/**
* Standalone routine to
* compute the average information theoretic measure
* for a single agent in a multivariate time series.
* Returns the average.
* This method suitable for heterogeneous agents.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param col index of the given variable
* @return average of the measure for the given variable.
*/
public double computeAverageLocal(int states[][], int col);
/**
* Standalone routine to
* compute the average information theoretic measure
* for a single agent in a multivariate time series.
* Returns the average.
* This method suitable for heterogeneous agents.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param index1 row index of the given variable
* @param index2 column index of the given variable
* @return average of the measure for the given variable.
*/
public double computeAverageLocal(int states[][][], int index1, int index2);
}

View File

@ -19,59 +19,111 @@
package infodynamics.measures.discrete;
/**
* Combines functionality for single agents with functionality
* required in the context of the past.
* A base class for calculators computing measures for
* a single variable which
* require knowledge of the embedded past state of a univariate
* discrete (ie int[]) variable.
*
* @author Joseph Lizier
*
* <p>This combines functionality for single agents from
* {@link SingleAgentMeasure} with functionality
* required in the context of the past provided by
* {@link ContextOfPastMeasureCalculator}.</p>
*
* <p>Usage is as defined in {@link InfoMeasureCalculator}, with
* extra methods for supplying observations and making
* calculations defined in {@link SingleAgentMeasure}</p>.
*
* <p>Users should not need to deal with this class directly;
* it is simply used to gather common functionality for several
* child classes.
* </p>
*
* TODO Make the Active info storage and entropy calculators inherit from this
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public abstract class SingleAgentMeasureInContextOfPastCalculator extends
ContextOfPastMeasureCalculator implements SingleAgentMeasure {
/**
* Construct the calculator
*
* @param base number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
* @param history embedding length
*/
public SingleAgentMeasureInContextOfPastCalculator(int base, int history) {
super(base, history);
}
@Override
public final double[] computeLocal(int[] states) {
initialise();
addObservations(states);
return computeLocalFromPreviousObservations(states);
}
@Override
public final double[][] computeLocal(int[][] states) {
initialise();
addObservations(states);
return computeLocalFromPreviousObservations(states);
}
@Override
public final double[][][] computeLocal(int[][][] states) {
initialise();
addObservations(states);
return computeLocalFromPreviousObservations(states);
}
@Override
public final double computeAverageLocal(int[] states) {
initialise();
addObservations(states);
return computeAverageLocalOfObservations();
}
@Override
public final double computeAverageLocal(int[][] states) {
initialise();
addObservations(states);
return computeAverageLocalOfObservations();
}
@Override
public final double computeAverageLocal(int[][][] states) {
initialise();
addObservations(states);
return computeAverageLocalOfObservations();
}
public final double[] computeLocalAtAgent(int[][] states, int col) {
@Override
public final double[] computeLocal(int[][] states, int col) {
initialise();
addObservations(states, col);
return computeLocalFromPreviousObservations(states, col);
}
public final double[] computeLocalAtAgent(int[][][] states, int index1, int index2) {
@Override
public final double[] computeLocal(int[][][] states, int index1, int index2) {
initialise();
addObservations(states, index1, index2);
return computeLocalFromPreviousObservations(states, index1, index2);
}
public final double computeAverageLocalAtAgent(int[][] states, int col) {
@Override
public final double computeAverageLocal(int[][] states, int col) {
initialise();
addObservations(states, col);
return computeAverageLocalOfObservations();
}
@Override
public final double computeAverageLocal(int[][][] states, int index1, int index2) {
initialise();
addObservations(states, index1, index2);
return computeAverageLocalOfObservations();
}
}

View File

@ -27,60 +27,76 @@ import infodynamics.utils.EmpiricalMeasurementDistribution;
import infodynamics.utils.RandomGenerator;
/**
* <p>Implements transfer entropy (see Schreiber, PRL, 2000)
* and local transfer entropy (see Lizier et al, PRE, 2008).
* We use the term <i>apparent</i> transfer entropy to mean that
* we compute the transfer that appears to come from a single
* source variable, without examining any other potential sources
* (see Lizier et al, PRE, 2008). This is also known as <i>pairwise</i>
* transfer entropy.</p>
*
* <p>Specifically, this implements the transfer entropy for
* <i>discrete</i>-valued variables.</p>
*
* <p>Usage:
* <ol>
* <li>Construct: {@link #TransferEntropyCalculator(int, int)}</li>
* <li>Initialise: {@link #initialise()}</li>
* <li>Either:
* <ol>
* <li>Continuous accumulation of observations then measurement; call:
* <ol>
* <li>{@link #addObservations(int[], int[])} or related calls
* several times over - <b>note:</b> each method call adding
* observations can be viewed as updating the PDFs; they do not
* append the separate time series (this would be incorrect behaviour
* for the transfer entropy, since the start of one time series
* is not necessarily related to the end of the other).</li>
* <li>The compute relevant quantities, e.g.
* {@link #computeLocalFromPreviousObservations(int[], int[])} or
* {@link #computeAverageLocalOfObservations()}</li>
* </ol>
* <li>or Standalone computation from a single set of observations;
* call e.g.: {@link #computeLocal(int[], int[])} or
* {@link #computeAverageLocal(int[][], int)}.>/li>
* </ol>
* </ol>
* <p>Implements <b>transfer entropy</b>
* for univariate discrete time-series data.
* That is, it is applied to <code>int[]</code> data, indexed
* by time.
* See Schreiber below for the definition of transfer entropy,
* and Lizier et al. for the definition of local transfer entropy.
* Specifically, this class implements the pairwise or <i>apparent</i>
* transfer entropy; i.e. we compute the transfer that appears to
* come from a single source variable, without examining any other
* potential sources
* (see Lizier et al, PRE, 2008).</p>
*
* <p>
* Usage of the child classes implementing this interface is intended to follow this paradigm:
* </p>
* <ol>
* <li>Construct the calculator via {@link #TransferEntropyCalculator(int, int)}
* or {@link #TransferEntropyCalculator(int, int, int)};</li>
* <li>Initialise the calculator using
* {@link #initialise()};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* the set of {@link #addObservations(int[], int[])} methods, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average TE: {@link #computeAverageLocalOfObservations()};</li>
* <li>the local TE values for these samples: {@link #computeLocalOfPreviousObservations()}</li>
* <li>local TE values for a specific set of samples: e.g.
* {@link #computeLocalFromPreviousObservations(int[], int[])} etc.</li>
* <li>the distribution of TE values under the null hypothesis
* of no relationship between source and
* destination values: {@link #computeSignificance(int)} or
* {@link #computeSignificance(int[][])}.</li>
* </ul>
* </li>
* <li>As an alternative to steps 3 and 4, the user may undertake
* standalone computation from a single set of observations, via
* e.g.: {@link #computeLocal(int[], int[])} or
* {@link #computeAverageLocal(int[][], int)}.</li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* TODO Add arbitrary source-dest delay
*
* @see "Schreiber, Physical Review Letters 85 (2) pp.461-464, 2000;
* <a href='http://dx.doi.org/10.1103/PhysRevLett.85.461'>download</a>
* (for definition of transfer entropy)"
* @see "Lizier, Prokopenko and Zomaya, Physical Review E 77, 026110, 2008;
* <a href='http://dx.doi.org/10.1103/PhysRevE.77.026110'>download</a>
* (for definition of <i>local</i> transfer entropy and qualification
* of naming it as <i>apparent</i> transfer entropy)"
* <p><b>References:</b><br/>
* <ul>
* <li>T. Schreiber, <a href="http://dx.doi.org/10.1103/PhysRevLett.85.461">
* "Measuring information transfer"</a>,
* Physical Review Letters 85 (2) pp.461-464, 2000.</li>
* <li>J. T. Lizier, M. Prokopenko and A. Zomaya,
* <a href="http://dx.doi.org/10.1103/PhysRevE.77.026110">
* "Local information transfer as a spatiotemporal filter for complex systems"</a>
* Physical Review E 77, 026110, 2008.</li>
* </ul>
*
* @author Joseph Lizier, <a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>
*
*/
public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
implements ChannelCalculator, AnalyticNullDistributionComputer {
/**
* Counts of (source,dest_next,dest_embedded_past) tuples
*/
protected int[][][] sourceNextPastCount = null; // count for (source[n],dest[n+1],dest[n]^k) tuples
/**
* Counts of (source,dest_embedded_past) tuples
*/
protected int[][] sourcePastCount = null; // count for (source[n],dest[n]^k) tuples
/**
* Whether to assume periodic boundary conditions for channels across
@ -124,7 +140,8 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
* @param base
* @param destHistoryEmbedLength
*
* @return
* @return a new TransferEntropyCalculator object
* @deprecated
*/
public static TransferEntropyCalculator newInstance(int base, int destHistoryEmbedLength) {
@ -143,7 +160,7 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Create a new TE calculator for the given base and destination history embedding length.
*
* @param base number of quantisation levels for each variable.
* @param base number of symbols for each variable.
* E.g. binary variables are in base-2.
* @param destHistoryEmbedLength embedded history length of the destination to condition on -
* this is k in Schreiber's notation.
@ -170,7 +187,7 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Create a new TE calculator for the given base and destination history embedding length.
* Create a new TE calculator for the given base, destination and source history embedding lengths.
*
* @param base number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
@ -207,12 +224,7 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
startObservationTime = Math.max(Math.max(k, sourceHistoryEmbedLength), 1);
}
/**
* Initialise calculator, preparing to take observation sets in
* Should be called prior to any of the addObservations() methods.
* You can reinitialise without needing to create a new object.
*
*/
@Override
public void initialise(){
super.initialise();
estimateComputed = false;
@ -221,12 +233,7 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
MatrixUtils.fill(sourcePastCount, 0);
}
/**
* Add observations for a single source-destination pair
* to our estimates of the pdfs.
* @param source source timte series
* @param dest destination time series
*/
@Override
public void addObservations(int[] source, int[] dest) {
int rows = dest.length;
// increment the count of observations:
@ -270,9 +277,11 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Add observations for a single source-destination pair
* to our estimates of the pdfs.
* @param source source timte series
* @param dest destination time series
* @param valid time series of whether the signals
*
* @param source source time-series
* @param dest destination time-series.
* Must be same length as source
* @param valid time-series of whether the signals
* at the given time should be considered valid
* and added to our PDFs
*/
@ -346,16 +355,17 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Add observations for a single source-destination pair of the multi-agent system
* Add observations for a single source-destination pair
* to our estimates of the pdfs.
* This call is for time series not part of the same 2D array.
* Start and end time are the (inclusive) indices within which to add the observations.
* The start time is from the earliest of the k historical values of the destination (inclusive),
* the end time is the last destination time point to add in.
* @param source
* @param dest
* @param startTime
* @param endTime
*
* @param source source time-series
* @param dest destination time-series.
* Must be same length as source
* @param startTime earliest time that we may extract embedded history from
* @param endTime last destination (next) time point to add in
*
*/
public void addObservations(int[] source, int[] dest, int startTime, int endTime) {
@ -398,13 +408,17 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Add observations in to our estimates of the pdfs.
* Add observations in to our estimates of the PDFs,
* from a multivariate time-series.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
* variable pairs separated by j column will contribute to the PDFs.
*
* @param states 1st index is time, 2nd index is agent number
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param j - number of columns to compute transfer entropy across
* (i.e. src is column i-j, dest is column i: transfer is j cells to the right)
* (i.e. source is column i-j, dest is column i: we
* compute transfer is j cells to the right, using observations
* across all column pairs separated by j)
*/
public void addObservations(int states[][], int j) {
int timeSteps = states.length;
@ -480,14 +494,19 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Add observations in to our estimates of the pdfs.
* Add observations in to our estimates of the PDFs,
* from a multivariate time-series.
* This call suitable only for homogeneous agents, as all
* agents will contribute to single pdfs.
* variable pairs separated by h rows and j columns
* will contribute to the PDFs.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param h - number of rows to compute transfer entropy across
* (i.e. source is in row i-h, dest is column i)
* @param j - number of columns to compute transfer entropy across
* (i.e. src (g-h,i-j), dest (g,i): transfer is h cells down, j cells to the right)
* (i.e. source is column i-j, dest is column i)
*/
public void addObservations(int states[][][], int h, int j) {
int timeSteps = states.length;
@ -598,12 +617,13 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Add observations for a single source-destination pair of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
* This call should be made as opposed to {@link #addObservations(int[][], int)}
* for computing TE for heterogeneous agents.
*
* @param states 1st index is time, 2nd index is agent number
* @param sourceIndex source agent index
* @param destIndex destination agent index
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param sourceIndex source variable index in states
* @param destIndex destination variable index in states
*/
public void addObservations(int states[][], int sourceIndex, int destIndex) {
int rows = states.length;
@ -648,14 +668,16 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Add observations for a single source-destination pair of the multi-agent system
* to our estimates of the pdfs.
* This call should be made as opposed to addObservations(int states[][])
* for computing active info for heterogeneous agents.
* This call should be made as opposed to {@link #addObservations(int[][][], int, int)}
* for computing TE for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param sourceRowIndex source agent row index
* @param sourceColumnIndex source agent column index
* @param destRowIndex destination agent row index
* @param destColumnIndex destination agent column index
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param sourceRowIndex source variable row index in states
* @param sourceColumnIndex source variable column index in states
* @param destRowIndex destination variable row index in states
* @param destColumnIndex destination variable column index in states
*/
public void addObservations(int states[][][], int sourceRowIndex, int sourceColumnIndex,
int destRowIndex, int destColumnIndex) {
@ -700,24 +722,27 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
*
* Returns the count of observations of the past given state dest[n]^k.
* The past state is indicated by a discrete integer representing the joint variable
* Returns the count of observations of the supplied past state
* pastVal.
* The past state is indicated by a unique discrete integer representing the joint variable
* of the k past states: (dest[n-k+1],dest[n-k+2],...,dest[n-1],dest[n]).
* The integer is computed as:<br/>
* pastVal = dest[n-k+1] * base^(k-1) + dest[n-k+2] * base^(k-2) + ... + dest[n-1] * base + dest[n]
*
*
* @param pastVal joint state of the past of the destination dest[n]^k
* @return count of observations of the given past state
* @param pastVal int representing the joint state of the past of the destination dest[n]^k
* @return count of observations of this given past state
*/
public int getPastCount(int pastVal) {
return pastCount[pastVal];
}
/**
* Returns the probability of the supplied past state
* pastVal.
* See {@link #getPastCount(int)} for how the joint value representing the past is calculated.
*
* @see {@link #getPastCount(int)} for how the joint value representing the past is calculated.
* @param pastVal joint state of the past of the destination dest[n]^k.
* @param pastVal int representing the joint state of the past of the destination dest[n]^k
* @return probability of the given past state
*/
public double getPastProbability(int pastVal) {
@ -725,13 +750,12 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Returns the count of observations of the past given past state and next value.
*
* Returns the count of observations of the past given state dest[n]^k and next state dest[n+1].
*
* @see {@link #getPastCount(int)} for how the joint value representing the past is calculated.
* See {@link #getPastCount(int)} for how the joint value representing the past is calculated.
*
* @param destVal next state of the destination dest[n+1]
* @param pastVal joint state of the past of the destination dest[n]^k
* @param pastVal int representing the joint state of the past of the destination dest[n]^k
* @return count of observations of the given past state and next state
*/
public int getNextPastCount(int destVal, int pastVal) {
@ -739,9 +763,12 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Returns the probability of the past given past state and next value.
*
* See {@link #getPastCount(int)} for how the joint value representing the past is calculated.
*
* @param destVal next state of the destination dest[n+1]
* @param pastVal joint state of the past of the destination dest[n]^k
* @param pastVal int representing the joint state of the past of the destination dest[n]^k
* @return probability of the given past state and next state
*/
public double getNextPastProbability(int destVal, int pastVal) {
@ -749,12 +776,12 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
*
* Returns the count of observations of the past given state dest[n]^k and the source state source[n]^l.
* @see {@link #getPastCount(int)} for how the joint values representing the past are calculated.
*
* @param sourceVal joint state of the source source[n]^l
* @param pastVal joint state of the past of the destination dest[n]^k
* See {@link #getPastCount(int)} for how the joint values representing the past states are calculated.
*
* @param sourceVal int representing the joint state of the source source[n]^l
* @param pastVal int representing the joint state of the past of the destination dest[n]^k
* @return count of observations of the given past state and the source state
*/
public int getSourcePastCount(int sourceVal, int pastVal) {
@ -762,10 +789,12 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Returns the probability of the past given state dest[n]^k and the source state source[n]^l.
*
* @see {@link #getPastCount(int)} for how the joint values representing the past are calculated.
* @param sourceVal joint state of the source source[n]^l
* @param pastVal joint state of the past of the destination dest[n]^k
* See {@link #getPastCount(int)} for how the joint values representing the past states are calculated.
*
* @param sourceVal int representing the joint state of the source source[n]^l
* @param pastVal int representing the joint state of the past of the destination dest[n]^k
* @return probability of the given past state and the source state
*/
public double getSourcePastProbability(int sourceVal, int pastVal) {
@ -773,14 +802,14 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
*
* Returns the count of observations of the past given state dest[n]^k,
* the next state of the destination dest[n+1] and the source state source[n]^l.
* @see {@link #getPastCount(int)} for how the joint values representing the past are calculated.
*
* See {@link #getPastCount(int)} for how the joint values representing the past states are calculated.
*
* @param sourceVal state of the source source[n]
* @param sourceVal int representing the joint state of the source source[n]^l
* @param nextVal next state of the destination dest[n+1]
* @param pastVal joint state of the past of the destination dest[n]^k
* @param pastVal int representing the joint state of the past of the destination dest[n]^k
* @return count of observations of the given past state, next state of destination and the source state
*/
public int getSourceNextPastCount(int sourceVal, int destVal, int pastVal) {
@ -788,11 +817,14 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Returns the probability of the past given state dest[n]^k,
* the next state of the destination dest[n+1] and the source state source[n]^l.
*
* See {@link #getPastCount(int)} for how the joint values representing the past states are calculated.
*
* @see {@link #getPastCount(int)} for how the joint values representing the past are calculated.
* @param sourceVal state of the source source[n]^l
* @param sourceVal int representing the joint state of the source source[n]^l
* @param nextVal next state of the destination dest[n+1]
* @param pastVal joint state of the past of the destination dest[n]^k
* @param pastVal int representing the joint state of the past of the destination dest[n]^k
* @return probability of the given past state, next state of destination and the source state
*/
public double getSourceNextPastProbability(int sourceVal, int destVal, int pastVal) {
@ -800,7 +832,6 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
*
* Returns the count of observations of the next state dest[n+1].
*
* @param nextVal next state of the destination dest[n+1]
@ -811,6 +842,7 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
/**
* Returns the probability of the next state dest[n+1].
*
* @param nextVal state of the next destination dest[n+1]
* @return probability of the given next state
@ -819,12 +851,7 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
return (double) nextCount[destVal] / (double) observations;
}
/**
* Returns the average local transfer entropy from
* the observed values which have been passed in previously.
*
* @return
*/
@Override
public double computeAverageLocalOfObservations() {
double te = 0.0;
double teCont = 0.0;
@ -883,7 +910,7 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
* Returns the average active information storage from
* the observed values which have been passed in previously.
*
* @return
* @see ActiveInformationCalculator
*/
public double computeAverageActiveInfoStorageOfObservations() {
double active = 0.0;
@ -931,15 +958,7 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
}
}
/**
* Compute the significance of obtaining the given average TE from the given observations
*
* This is as per Chavez et. al., "Statistical assessment of nonlinear causality:
* application to epileptic EEG signals", Journal of Neuroscience Methods 124 (2003) 113-128.
*
* @param numPermutationsToCheck number of new orderings of the source values to compare against
* @return
*/
@Override
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) {
double actualTE = computeAverageLocalOfObservations();
@ -1030,12 +1049,14 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Computes local transfer entropy for the given values
* @param sourceCurrent
* @param destNext
* @param destPast
*
* @see {@link #getPastCount(int)} for how the joint values representing the past are calculated.
* @return
*
* See {@link #getPastCount(int)} for how the joint values representing the past are calculated.
*
* @param sourceCurrent int representing the joint state of the source source[n]^l
* @param destNext next state of the destination dest[n+1]
* @param destPast int representing the joint state of the past of the destination dest[n]^k
*
* @return local TE for the given observation
*/
public double computeLocalFromPreviousObservations(int sourceCurrent, int destNext, int destPast){
@ -1046,12 +1067,13 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Computes local apparent transfer entropy for the given
* states, using pdfs built up from observations previously
* states, using PDFs built up from observations previously
* sent in via the addObservations method.
* This method to be used for homogeneous agents only
* @param states 1st index is time, 2nd index is agent number
*
* @return
* @param source source time-series
* @param dest destination time-series.
* Must be same length as source
* @return time-series of local TE values
*/
public double[] computeLocalFromPreviousObservations(int sourceStates[], int destStates[]){
int timeSteps = destStates.length;
@ -1105,13 +1127,20 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Computes local transfer for the given
* states, using pdfs built up from observations previously
* multivariate states, using pdfs built up from observations previously
* sent in via the addObservations method.
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd index is agent number
* @param j columns across which to compute TE
* @return
* This call suitable only for homogeneous agents, as all
* variable pairs separated by j column will
* have their local TE computed.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param j - number of columns to compute transfer entropy across
* (i.e. source is column i-j, dest is column i: we
* compute transfer is j cells to the right, using observations
* across all column pairs separated by j)
* @return multivariate time series of local TE values
* (first index is time, second index is destination variable)
*/
public double[][] computeLocalFromPreviousObservations(int states[][], int j){
int timeSteps = states.length;
@ -1200,13 +1229,20 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
* Computes local transfer for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method.
* This method to be used for homogeneous agents only
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* This call suitable only for homogeneous agents, as all
* variable pairs separated by h rows and j columns
* will have their local TE computed.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param h - number of rows to compute transfer entropy across
* (i.e. source is in row i-h, dest is column i)
* @param j - number of columns to compute transfer entropy across
* (i.e. src (g-h,i-j), dest (g,i): transfer is h cells down, j cells to the right)
* @return
* (i.e. source is column i-j, dest is column i)
* @return multivariate time series of local TE values
* (first index is time, second index is destination variable
* row number, third is destination variable column number)
*/
public double[][][] computeLocalFromPreviousObservations(int states[][][], int h, int j){
int timeSteps = states.length;
@ -1322,12 +1358,17 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Computes local transfer for the given
* states, using pdfs built up from observations previously
* sent in via the addObservations method.
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd index is agent number
* @return
* single source-destination pair of the multi-agent system,
* using pdfs built up from observations previously
* sent in via the addObservations methods.
* This call should be made as opposed to {@link #addObservations(int[][], int)}
* for computing local TE for heterogeneous agents.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param sourceIndex source variable index in states
* @param destIndex destination variable index in states
* @return time-series of local TE values between the series
*/
public double[] computeLocalFromPreviousObservations(int states[][], int sourceCol, int destCol){
int rows = states.length;
@ -1383,16 +1424,20 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Computes local transfer for the given
* states, using pdfs built up from observations previously
* single source-destination pair of the multi-agent system,
* using pdfs built up from observations previously
* sent in via the addObservations method.
* This method is suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param sourceRowIndex source agent row index
* @param sourceColumnIndex source agent column index
* @param destRowIndex destination agent row index
* @param destColumnIndex destination agent column index
* @return
* This call should be made as opposed to {@link #addObservations(int[][][], int, int)}
* for computing local TE for heterogeneous agents.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param sourceRowIndex source variable row index in states
* @param sourceColumnIndex source variable column index in states
* @param destRowIndex destination variable row index in states
* @param destColumnIndex destination variable column index in states
* @return time-series of local TE values between the series
*/
public double[] computeLocalFromPreviousObservations(int states[][][],
int sourceRowIndex, int sourceColumnIndex, int destRowIndex, int destColumnIndex){
@ -1451,11 +1496,12 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
* Standalone routine to
* compute local transfer entropy between two time series
* Return a time series of local values.
* First history rows are zeros
* @param sourceStates time series of source states
* @param destStates time series of destination states
* First max(k,l) values are zeros since TE is not defined there
*
* @return
* @param sourceStates source time-series
* @param destStates destination time-series.
* Must be same length as sourceStates
* @return time-series of local TE values
*/
public double[] computeLocal(int sourceStates[], int destStates[]) {
@ -1467,14 +1513,21 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Standalone routine to
* compute local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* array of the states of homogeneous agents.
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method to be called for homogeneous agents only
* First max(k,l) values are zeros since TE is not defined there.
* This call suitable only for homogeneous agents, as all
* variable pairs separated by j column will
* have their local TE computed.
*
* @param states - 2D array of states
* @param j number of columns across which to compute the TE
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param j - number of columns to compute transfer entropy across
* (i.e. source is column i-j, dest is column i: we
* compute transfer is j cells to the right, using observations
* across all column pairs separated by j)
* @return multivariate time series of local TE values
* (first index is time, second index is destination variable)
*/
public double[][] computeLocal(int states[][], int j) {
@ -1488,14 +1541,21 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
* compute local transfer entropy across a 3D spatiotemporal
* array of the states of homogeneous agents.
* Return a 3D spatiotemporal array of local values.
* First history rows are zeros
* This method to be called for homogeneous agents only
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* First max(k,l) values are zeros since TE is not defined there.
* This call suitable only for homogeneous agents, as all
* variable pairs separated by h rows and j columns
* will have their local TE computed.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param h - number of rows to compute transfer entropy across
* (i.e. source is in row i-h, dest is column i)
* @param j - number of columns to compute transfer entropy across
* (i.e. src (g-h,i-j), dest (g,i): transfer is h cells down, j cells to the right)
* @return
* (i.e. source is column i-j, dest is column i)
* @return multivariate time series of local TE values
* (first index is time, second index is destination variable
* row number, third is destination variable column number)
*/
public double[][][] computeLocal(int states[][][], int h, int j) {
@ -1508,12 +1568,18 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
* Standalone routine to
* compute average local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* This method to be called for homogeneous agents only
* Return the average TE.
* This call suitable only for homogeneous agents, as all
* variable pairs separated by j column will
* have their local TE computed.
*
* @param states - 2D array of states
* @param j - TE across j cells to the right
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param j - number of columns to compute transfer entropy across
* (i.e. source is column i-j, dest is column i: we
* compute transfer is j cells to the right, using observations
* across all column pairs separated by j)
* @return average TE across j variables to the right
*/
public double computeAverageLocal(int states[][], int j) {
@ -1526,13 +1592,18 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
* Standalone routine to
* compute average local transfer entropy across a 3D spatiotemporal
* array of the states of homogeneous agents
* Return the average
* This method to be called for homogeneous agents only
* Return the average.
* This call suitable only for homogeneous agents, as all
* variable pairs separated by h rows and j columns
* will have their PDFs combined.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param h - number of rows to compute transfer entropy across
* (i.e. source is in row i-h, dest is column i)
* @param j - number of columns to compute transfer entropy across
* (i.e. src (g-h,i-j), dest (g,i): transfer is h cells down, j cells to the right)
* (i.e. source is column i-j, dest is column i)
* @return
*/
public double computeAverageLocal(int states[][][], int h, int j) {
@ -1544,16 +1615,15 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Standalone routine to
* compute local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method suitable for heterogeneous agents
*
* @param states - 2D array of states
* @param sourceCol - column index for the source agent
* @param destCol - column index for the destination agent
* @return
* compute local transfer entropy between specific variables in
* a 2D spatiotemporal multivariate time-series.
* First max(k,l) values are zeros since TE is not defined there.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param sourceCol source variable index in states
* @param destCol destination variable index in states
* @return time-series of local TE values between the series
*/
public double[] computeLocal(int states[][], int sourceCol, int destCol) {
@ -1564,18 +1634,18 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Standalone routine to
* compute local transfer entropy across a 3D spatiotemporal
* array of the states of homogeneous agents
* Return a 2D spatiotemporal array of local values.
* First history rows are zeros
* This method suitable for heterogeneous agents
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param sourceRowIndex source agent row index
* @param sourceColumnIndex source agent column index
* @param destRowIndex destination agent row index
* @param destColumnIndex destination agent column index
* @return
* computes local transfer for the given
* single source-destination pair of the 3D multi-agent system.
* This method suitable for heterogeneous variables.
*
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param sourceRowIndex source variable row index in states
* @param sourceColumnIndex source variable column index in states
* @param destRowIndex destination variable row index in states
* @param destColumnIndex destination variable column index in states
* @return time-series of local TE values between the series
*/
public double[] computeLocal(int states[][][], int sourceRowIndex, int sourceColumnIndex,
int destRowIndex, int destColumnIndex) {
@ -1588,15 +1658,16 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Standalone routine to
* compute average local transfer entropy across a 2D spatiotemporal
* array of the states of homogeneous agents
* Returns the average
* compute local transfer entropy between specific variables in
* a 2D spatiotemporal multivariate time-series.
* Returns the average.
* This method suitable for heterogeneous agents.
*
* @param states - 2D array of states
* @param sourceCol - column index for the source agent
* @param destCol - column index for the destination agent
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable number)
* @param sourceCol source variable index in states
* @param destCol destination variable index in states
* @return average TE for the given pair
*/
public double computeAverageLocal(int states[][], int sourceCol, int destCol) {
@ -1607,17 +1678,19 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
/**
* Standalone routine to
* compute average local transfer entropy across a 3D spatiotemporal
* array of the states of homogeneous agents
* Returns the average
* This method suitable for heterogeneous agents
* compute local transfer entropy between specific variables in
* a 3D spatiotemporal multivariate time-series.
* Returns the average.
* This method suitable for heterogeneous agents.
*
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
* @param sourceRowIndex source agent row index
* @param sourceColumnIndex source agent column index
* @param destRowIndex destination agent row index
* @param destColumnIndex destination agent column index
* @return
* @param states multivariate time series
* (1st index is time, 2nd index is variable row number,
* 3rd is variable column number)
* @param sourceRowIndex source variable row index in states
* @param sourceColumnIndex source variable column index in states
* @param destRowIndex destination variable row index in states
* @param destColumnIndex destination variable column index in states
* @return average TE for the given pair
*/
public double computeAverageLocal(int states[][][], int sourceRowIndex, int sourceColumnIndex,
int destRowIndex, int destColumnIndex) {
@ -1627,9 +1700,21 @@ public class TransferEntropyCalculator extends ContextOfPastMeasureCalculator
return computeAverageLocalOfObservations();
}
/**
* Whether we assume periodic boundary conditions in the calls
* for homogeneous variables.
*
* @return as above
*/
public boolean isPeriodicBoundaryConditions() {
return periodicBoundaryConditions;
}
/**
* set whether we assume periodic boundary conditions in the calls
* for homogeneous variables.
*
* @param periodicBoundaryConditions as above
*/
public void setPeriodicBoundaryConditions(boolean periodicBoundaryConditions) {
this.periodicBoundaryConditions = periodicBoundaryConditions;
}